Chapter 12

Programming Wisdom

When I interview experienced programmers I always expect to enjoy the experience. Regardless of our personal backgrounds, I know that we will have some important things in common. In all likelihood we will both have:

  • Struggled with elusive bugs
  • Been asked to write code from unclear requirements
  • Been asked for estimates without any tangible requirements
  • Faced unrealistic deadlines
  • Had many “ah-hah!” moments
  • Feelings of nostalgia over old, superseded tools
  • Feelings of great satisfaction from coding it, and…it works

I could fill an entire book with a list of experiences shared by programmers around the world, regardless of where they come from or what software they write. The questions in this chapter are all about exploring this common ground. These questions are different from other questions in this book because the answers to most of them are subjective. Almost certainly you will find an answer (or maybe even the wording of a question) in this chapter with which you strongly disagree. Well, that's fine, and I look forward to debating the point with you online or perhaps even in person.

QUESTIONS

The worst answer you can give to any of these questions is a dismissive shrug. If an interviewer asks for your opinion, be sure that you give it, even if your opinion is that it (whatever it is) doesn't matter. Be ready to defend your point of view as if you were debating with a fellow programmer, perhaps as if you were debating with someone in your development team. In this way, both you and the interviewer get to see what it will be like working with each other. There's no point submissively deferring to everything the interviewer says unless that is the kind of working relationship you're happy to have. I hope it isn't.

1. Why use source control?
What arguments would you make to convince a non-technical manager that your development team should adopt source code revision control?
2. What popular programming wisdom do you disagree with?
You might have heard the story of the little girl who asks her mother why she cuts the legs and wings off the turkey before putting it in the oven. The girl's mother says that she learned how to cook a turkey from the girls' grandmother. The girl's grandmother explains that she was taught by her own mother, and so on, until the girl discovers that her great-great-great grandmother cut the legs and wings off the turkey because, otherwise, it would not fit into the very small oven she owned 100 years ago.
A lot of programming wisdom is like this. There might once have been a good reason to do a certain thing but it was long ago. Whenever you receive some wisdom you should pause to consider whether the reasoning behind this wisdom as valid today as it was back then.
As an experienced programmer, what are some examples of received wisdom that you don't agree with?
3. Why are software projects always late?
If one thing is true of software projects it is that they are usually late and over budget. Why is this the case?
4. Why can't you keep adding programmers?
Why can't you keep adding programmers to a software project to bring it in on time?
5. How do you ensure that your estimates are sound?
As a programmer responsible for giving estimates that are as accurate as possible, how do you ensure that your estimates are sound?
6. Why is code clarity important?
Everyone agrees that code clarity is important, but not everyone agrees what it means exactly. Give a definition of “code clarity” and explain why it is important.
7. What are your red flags during code review?
When you review another programmer's code, what are the things you look for that might indicate a deeper problem? In other words, what are your red flags when reviewing code?
8. Describe some things you always do when troubleshooting
Some programmers seem to have a knack for troubleshooting. When a customer reports a problem that can't be reproduced in the testing environment, these savant programmers will start twitching and blinking, and before you know it they have guessed the problem and are halfway done coding a solution.
The rest of us, unfortunately, need to work a bit harder.
What are some things that you always do (or try to do) when diagnosing a problem?
9. How do you go about getting familiar with a large code project?
Every time you change jobs a good chance exists that you will need to quickly become familiar with a large new code base.
How do you do that?
10. Describe your understanding of cargo-cult programming
The phrase cargo-cult programming (or cargo-cult software engineering) is sometimes used to describe an undesirable approach to programming.
Describe what the term means and why this approach is undesirable.
11. Describe some potential downsides of code comments
When students are taught programming at school, one of the first lessons they learn is that they should make sure to include appropriate code comments.
This is probably good advice, but what are some of the potential downsides of code comments?
12. When is it acceptable to produce lower-quality code?
Everyone agrees that programmers should strive to write good-quality code. Everyone might not agree on what this means, exactly, but in general all of us prefer to write good code rather than bad code.
Can you think of a valid, ethical reason why you would be motivated to write substandard code?
13. How can large code projects be kept under control?
Large software products always seem to contain a lot of regrettable code, and this is especially true for successful products that are maintained over many years. Almost everyone who has worked on a large code base will have issues with it in some way or another. It might be that it has too many or too few layers of indirection, or that inconsistencies are throughout the code base, or any number of other problems.
As a programmer working on a large code project, how do you keep these kinds of problems under control?
14. How do you add features to unfamiliar code?
Describe how you would go about adding new features to a large, unfamiliar code project.
15. What exactly is wrong with so-called clever code?
Now and then every programmer will write a bit of code and feel justifiably proud of their achievement. This is part of the fun in programming. Sometimes, unfortunately, the pursuit of this happy feeling will lead programmers astray and cause us to write code that is, frankly, too clever.
What does it mean by code being “too clever,” and what exactly is wrong with clever code?
16. How do you improve your programming ability?
Suppose you want to improve your coding ability, how would you go about it?
17. Describe a coding project of which you're proud
Interviewers often ask about projects that made you proud. Talking about work-related projects is customary but most interviewers will let you talk about any programming project you've worked on, at work, at school, or even at home. The interviewer's aim is to give you a platform to display your enthusiasm for programming.
18. Explain programming in non-technical terms
Suppose you are at a family event and your grandmother asks you to explain what it is that you do as a “programmer.” What do you tell her?
19. Explain the significance of coupling and cohesion
You often hear these terms in debates about the implementation of an application. What is the programming-related meaning of the words coupling and cohesion, and why are they significant?
20. What is the real problem with global variables?
A global variable is a variable that is available throughout an entire application, regardless of where it is referenced.
That sounds quite handy, so what is the real problem with global variables?
21. Explain the term technical debt in terms a non-technical manager will understand
Most programmers love the term technical debt because it is an apt metaphor for a problem they face every day. Unfortunately, the significance of the term appears to not be quite as apparent to as many non-technical managers.
Explain the meaning and significance of technical debt in terms that a non-technical manager will understand.
22. Explain the term refactoring in terms that a non-technical manager will understand
The term refactoring is commonly used by many programmers and is generally understood to mean something positive. Explain in non-technical terms what the term means.
23. What is the significance of a leaky abstraction?
In 2002, Joel Spolsky wrote a blog post entitled The Law of Leaky Abstractions:
The term leaky abstraction has now entered the mainstream of programmer jargon.
Explain the meaning and significance of this term.
24. What is continuous integration and how is it helpful?
The concept of continuous integration has progressed to the point where you can buy specialized tools to support the practice.
What does the term continuous integration mean, and how is it helpful?
25. What is your favorite software development methodology?
once worked with a very plain-speaking programmer who snorted loudly when I asked a candidate this question at an interview. He explained afterwards that he thought the question was trendy and meaningless, and that the word methodology was a pretentious substitute for the word method. He was probably right, so here is the same question asked in a more plain-speaking way: In your experience, what methods most improve the effectiveness of a software development team?
26. How do I tell the product owner that his requirements are ridiculous?
Programmers are usually not backward in regard to assessing the merits of a requirement that appears to be unrealistic or impossible.
How would you explain to a product owner that you cannot implement his requirements?
27. What advice do you have for new programmers?
Suppose you were given the responsibility of mentoring a new and inexperienced programmer. What key bits of advice would you want to ensure this programmer understands as she embarks on her programming career?
28. Do coding standards influence code quality?
Do you think raising the quality of code by enforcing coding standards is possible?
29. Which coding standards are the most important?
Every software development team seems to have a set of coding standards. Describe some coding standards that you think are important for a team striving to produce good-quality code.
30. Why is the number of lines of code produced by a programmer a poor measurement of programmer productivity?
Every now and then it will occur to a non-technical manager that measuring the cost-effectiveness of a software development team ought to be possible. This manager might do some reading about the practice of programming and it will occur to him that because programmers write code, just like authors write books and lawyers write contracts, then deriving some useful information by counting the number of lines of code produced by a programmer over time should be possible.
Why is lines of code (LoC) a poor measurement of programmer productivity?
31. Is the goto statement really that harmful?
Most programmers have been instilled with a strong sense of certainty that the goto statement is a source of untold misery and that it must be avoided at all costs.
Explain why the goto statement is considered harmful.
32. Should software managers have technical backgrounds?
Should software development managers have technical backgrounds? To ask this another way, can a software development manager without a technical background be effective? (Notice that no stipulation exists that this technical background is necessarily programming experience.)
33. Should application architects know how to write code?
Similar to the question about technical and non-technical managers, another question I often hear is whether application architects should be capable of writing code. What is your opinion?
34. What is your desert-island best practice?
If you could choose one so-called “best practice” that everyone in your team (or, if you prefer, the world) would instantly start following, what would it be?

ANSWERS

1. Why use source control?
What arguments would you make to convince a non-technical manager that your development team should adopt source code revision control?
From experience I have found that non-technical managers respond best to analogies to which they can relate. Most managers will use word-processing software, and everyone who uses word-processing software to write documents will eventually make a mistake they want to fix. Fortunately, every word processor has an undo function so that accidents, even big ones, can be undone at the click of a button. No one would seriously think of a word-processor's undo function as a “nice to have” feature.
Revision control is the software developer's undo function. If you make a mistake it can be undone without any fuss.
But revision control is more than that.
You can go right back to a change that was made days or weeks or years ago and undo that change. You can go right back to the time the document was created. You can undo a change made weeks or years ago while retaining all the changes that were made since then. It's a very, very powerful undo feature.
But it's even more than that.
Suppose you need to produce two similar documents: one for public viewing and another for internal circulation. In a word processor you would write the first document, or part of it, then make a copy, then make changes in one or both documents. From the moment that you take a copy of the original document you need to consider whether a change to one document should be made to the other document as well. Every change has to be copied by hand from one document to another. If you have created more than one copy of the original document then you need to copy your change into each of these documents. Keeping documents in sync can be an error-prone and frustrating exercise.
Revision control solves this problem for the programmer. It tracks changes so it knows what needs to be copied between documents. It can show the programmer each of the differences between two documents and the programmer can pick and choose which changes should be copied and which should not. This feature of revision control software is called merging.
But revision control is even more than that.
Suppose that you have a large collection of documents and a number of projects in progress, each with a team of writers. There will be many changes that need to somehow be synchronized and a good chance exists that many of these changes will be in conflict. One team might have deleted a paragraph whereas another team has fixed some grammatical errors and added a new sentence to that paragraph. A third team might have replaced the paragraph with completely new wording whereas a fourth team might have moved it to the end of the document and converted it to a footnote. Resolving these changes by hand, well, it isn't something I would want to attempt, not for anything important.
Revision control solves that problem, too. It shows you who changed what, and when they changed it, and whether the programmers have been diligent with their changes. It even shows why they changed it. Revision control handles changes from multiple teams. It coordinates the distribution of those changes, all the while tracking every change that has ever been made.
Every software developer benefits from using revision control. It is one of the essential tools of software development, on par with a compiler and a text editor. If a business values its source code then it will use revision control. No valid reason exists to not use source code revision control.
2. What popular programming wisdom do you disagree with?
You might have heard the story of the little girl who asks her mother why she cuts the legs and wings off the turkey before putting it in the oven. The girl's mother says that she learned how to cook a turkey from the girls' grandmother. The girl's grandmother explains that she was taught by her own mother, and so on, until the girl discovers that her great-great-great grandmother cut the legs and wings off the turkey because, otherwise, it would not fit into the very small oven she owned 100 years ago.
A lot of programming wisdom is like this. There might once have been a good reason to do a certain thing, but it was long ago. Whenever you receive some wisdom you should pause to consider whether the reasoning behind this wisdom as valid today as it was back then.
As an experienced programmer, what are some examples of received wisdom that you don't agree with?
For a profession that has its origins in mathematics and science, a surprising amount of superstition masquerades as wisdom. The new programmer suffers the most because every experienced programmer has strong views on the right way to program, things that should never be done, and things that should always be done. Experienced programmers are always happy to share their wisdom with anyone who will listen, and quite often this wisdom is enshrined as a set of commandments entitled coding standards.
You will, over time, form your own opinion of what is good and what is bad, and that is just as it should be—any worthwhile programming experience will leave its mark on you. Your experience will inform your decisions, and sometimes your experiences will give you a gut feeling about why you should code one way or another, even if you can't articulate exactly why you chose to code the way you did.
But don't fall for the trap of thinking that your experiences are universal. Avoid thinking that your way is the best or the only right way. Retain some humility in your outlook, and continue to learn from new experiences even when they challenge your established habits and practices.
Here are some examples of received wisdom. Think about how your experience aligns with each of these. Think about the arguments on both sides (although usually more than two sides exist in any programming debate), and think about how you would react in an interview when asked the question “What programming wisdom do you disagree with?”
  • Code comments are essential and should be used liberally throughout all of your code.
  • You should never reinvent the wheel.
  • Modern programming should be a matter of finding the right combination of design patterns.
  • Global variables are always bad.
  • Goto statements should be banned.
  • In-line SQL must be avoided at all costs.
  • Methods must not consist of more than n lines of code.
  • Functional programming is superior to object-oriented programming.
  • Relational database technology has been superseded by XML and NoSQL technologies.
  • Code quality is always the most important thing.
  • Writing good software is an art.
  • Writing good software is a science.
  • You can't manage what you can't measure.
  • Software quality is a function of a good methodology.
  • Premature optimization is the root of all evil.
3. Why are software projects always late?
If one thing is true of software projects it is that they are usually late and over budget. Why is this the case?
For many experienced programmers the hardest part of answering this question at the interview will be giving a balanced answer while avoiding a tirade. Many reasons exist as to why software projects are late, and while some might be the fault of the programmer, many more are not.
Here are a few reasons to get you started. No doubt you will have a long list of your own.
Let's start with something the software industry has known since 1975 when Fred Brooks published The Mythical Man Month: If a project is in trouble, throwing money and people at it will almost always make things worse. Or in Brooks' words:

“…adding manpower to a late software project makes it later”

Software projects are complex, and new arrivals will take time to get up to speed. New arrivals will temporarily reduce the productivity of those who were already working on the project while they support the newcomers. As the number of people working on a project increases, so does the communication overhead. Every new person adds not one but many new channels of communication.
Another common reason for projects running late is that requirements keep changing. Building a house would be impossible if the plans kept changing throughout the build, and this is more or less true in software development.
The metaphor of building a house is (ironically) itself a reason why some projects run late. Software projects are estimated as if they were the same as building a brick wall. If you can estimate the number of bricks required and if you know the rate at which they can be placed then you can calculate with reasonable confidence how long it will take to construct a brick wall. Therefore, the reasoning goes, you can estimate with confidence how long it will take to construct this software. After all, you can count the number of features and screens and reports. You can estimate how long each will take to build based on how long it took to do something similar. You add up those estimates and add a bit of contingency—easy!
Software is a bit like construction, so the metaphor works to some extent, but it is different enough that the metaphor breaks down completely in the face of routine events. Consider how difficult estimating the time would be to build a brick wall if during the build:
  • You discover a problem with the bricks that makes them crumble if placed in stacks of 17 or 42 (there might be other problematic stack sizes, you don't know).
  • The brick manufacturer releases a critical brick fix, which means you have to tear down the partly completed wall and start again.
  • You realize halfway through construction that the bricks are not compatible with the mortar you've been using, even though both the bricks and the mortar are supposed to be industry standard and therefore compatible.
  • The build architect insists that you must queue the bricks rather than stack them. It takes you a week to figure out what this means.
  • Your client insists part way through the build that every alternate brick should be placed vertically rather than horizontally. They assumed you knew about this requirement so they didn't make it clear at the outset.
  • A tester tests your brick wall with blasts of dynamite and consequently raises several high-priority defects. It takes a week to resolve a debate about the validity of testing with explosives and another two weeks to rebuild the wall.
Another reason projects run late or get cancelled is poor risk management. Every project management approach has a theoretical answer to the question of dealing with project risk, and yet almost every project fails because an obvious risk was ignored or dismissed. How many times have you contributed to a risk log of some kind, only to see the risk log filed in a drawer (or the electronic equivalent) and never mentioned again, let alone acted on? Risk management is an active, ongoing process, and just like complex software projects there is no silver bullet.
I've discussed a few reasons why software projects are late. You should add your own experiences to this list. Here are few more ideas you can use to stimulate your thinking on this topic:
  • Excessive optimism
  • Unrealistic expectations
  • Poor communication
  • An absent project sponsor
  • A micro-managing project sponsor
  • Low team morale
  • Team inexperience
  • Technology immaturity (that is, “version 1.0”)
  • Unrecognized political/social antics
  • Incompetence
  • Scope creep (similar to changing requirements)
  • Lack of stakeholder involvement
  • Poor planning/no planning
4. Why can't you keep adding programmers?
Why can't you keep adding programmers to a software project to bring it in on time?
This question is often asked as a follow-up to the question of why software projects are always late.
A good way to answer this specific question is to highlight the nature of software development. Software project schedules are not driven by the number of people who are assigned to it.

I need this baby in a month—send me nine women!

Ed Guiness, writing on StackOverflow.com

Many other analogies exist:
  • You cannot take a photo any faster by assigning more photographers.
  • You cannot deliver a speech any quicker by adding more speakers.
  • After a construction site is filled with workers, you cannot add any more and hope to increase productivity; they will trip over each other and progress will slow down.
5. How do you ensure that your estimates are sound?
As a programmer responsible for giving estimates that are as accurate as possible, how do you ensure that your estimates are sound?
Estimating the duration of a project is perhaps one of the hardest aspects of software development to get right. It is also fraught with peril. A project might be cancelled or never started because of a bad estimate, and most of the misery experienced in software projects is due to unrealistic optimism crushing developers up against an immovable deadline.
So how should a programmer produce an estimate that is better than pure guesswork?
Well, at the very start of a project, before requirements have been agreed upon, a programmer's guess is about as good as it gets. That is not to say that a number should be chosen at random but that, until there is something to analyze, no real way exists for predicting what the project will involve, and therefore no way exists to predict what work will be required and how long that work might take.
Unfortunately, many programmers, particularly those at larger companies, will at some point find that they are assigned to a project where the overall project estimate was plucked out of the air by an optimistic salesperson even before the development team was formed. Expectations might have been set at the highest level of the business long before the first requirement was written down and certainly long before the first line of code has been written.
After the scope of a project has been agreed upon (if such a thing is possible—it depends on the project) it is only then that an estimate has a chance of being in the right ballpark.
It is self-evident that an estimate can be improved as a project progresses, based on the experience of the project team as they work on the project. The only point at which an estimate is guaranteed to be 100 percent accurate is when the project has finished. You could, therefore, take the approach that an estimate provided at the start of a project has a degree of uncertainty that is probably as large as it will ever be. This uncertainty, in theory if not in practice, will reduce as the project progresses toward a final outcome. Steve McConnell writing in Software Estimation: Demystifying the Black Art calls this the Cone of Uncertainty. McConnell writes:

An important—and difficult—concept is that the Cone of Uncertainty represents the best-case accuracy that is possible to have in software estimates at different points in a project. The Cone represents the error in estimates created by skilled estimators. It's easily possible to do worse. It isn't possible to be more accurate; it's only possible to be more lucky.

My own experience has been that the best, most accurate estimates come from a diligent effort to identify the tasks of a project and then to break those tasks down into small chunks of work. The estimates for these small chunks of work are then added up and adjusted by a risk factor that should tend toward pessimistic, especially for those things that are largely unknown or risky. These estimates should be (but rarely are) updated periodically throughout the project.
This is a gross oversimplification, but hopefully you get the idea that you should try to base your estimate on things you know will need to be done rather than any “gut feel.” Programmers, architects, and even project managers are notoriously optimistic about their team's ability to write code quickly.

Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law.

Douglas Hofstadter

One of the worst outcomes for a programmer who gives an estimate early in a project is when that estimate is taken as a promise. Software companies that make a living by quoting for projects will always factor in a large (larger than you might expect) padding factor to account for the unknown variables of a project. If too many unknown factors exist then most software companies will charge by time and materials, rather than risk losing money on an upfront quote that turns out to be horribly wrong. As programmers working in a team, you should take a similar approach. You should give estimates only when you have a reasonable understanding of what a project involves. If you don't have that understanding then you should postpone estimation until you get it. As professionals, to do anything else is to do a disservice to your customer, even if your customer is also your employer. Nobody benefits from a wildly inaccurate estimate, and a lot can be at stake.
If too much risk exists in a project (something you have to judge) then you should not give an estimate. In most cases this is easier said than done, but think of it this way: If you give an estimate that is out by a factor of 1000 percent then what is the likely outcome?
Here are some of the things you should consider every time you produce an estimate. A lot of these things are related to risk.
  • Has the team worked together before?
  • Is the team going to sit together? (Will all team members be in the same time zone?)
  • What tools will the team use?
  • Is an experienced technical leader full time on the project?
  • Is an experienced project leader full time on the project?
  • How new are the technologies involved?
  • Has the team any prior experience of a similar project?
  • How many features are required? What are they exactly?
  • What level of integration with third-party systems is required?
  • What constraints must be met, technical or otherwise?
  • What project management approach will be used (for example, SCRUM)?
  • How much of a dependency does this project have on third parties or systems outside of the team's control?
  • What kind of environment will the team work in, for example how often will the team be interrupted with non-project work?
  • Don't forget to allow for holidays and other foreseeable absences.

Note
If you haven't already read it, please do yourself a big favor and read Steve McConnell's book on this subject. It could save your career. His book is Software Estimation: Demystifying the Black Art, Microsoft Press, 2006 (ISBN: 978-0-7356-0535-0).

6. Why is code clarity important?
Everyone agrees that code clarity is important, but not everyone agrees what it means exactly. Give a definition ofcode clarityand explain why it is important.
If you think about the life cycle of a successful software product, you will quickly appreciate that much, much more time is spent maintaining and upgrading it than is spent writing the first version. If your code is hard to understand then you are creating more work for yourself or for the unfortunate programmer who inherits your code. This time and effort invariably has a real business cost attached to it, and this is certainly not just a theoretical cost.
A good way to answer this question, therefore, is to think of it from the maintenance programmer's point of view. If the maintenance programmer would find an item of code difficult to understand, and therefore difficult to work with, then the code probably isn't clear enough. Clear code, therefore, is any code that is readily understood by a competent maintenance programmer.
During an interview it will usually help to give an example of unclear code and show how this code could be made clearer.
Here's an example of unclear code presented back in Chapter 6:
 for ( int i=0 ; i < MyControl.TabPages.Count ; i++ )
 {
    MyControl.TabPages.Remove (MyControl.TabPages[i] );
    i--;
 }
This isn't a lot of code to look at, but even a casual glance should tell you that something funny is going on. The most obvious problem is that the loop index is modified inside the loop. Experience should tell you that this is unusual, even before you consider what the loop is trying to achieve.
You can refer to Chapter 6 for a closer look at this strange loop, but the clear version of this code is simply:
 MyControl.TabPages.Clear();
7. What are your red flags during code review?
When you review another programmer's code, what are the things you look for that might indicate a deeper problem? In other words, what are your red flags when reviewing code?
Every programmer has his own list of pet peeves. For many it is code formatting, perhaps the use of tabs instead of spaces. Others grind their teeth over easily fixed annoyances like the misalignment of a brace.
These things are annoying, but code formatting is easily fixed. Here are some things that can indicate deeper problems:
  • Empty catch blocks often indicate missing logic (the programmer knows an error might occur and chose to do nothing about it).
  • Meaningless names sometimes indicate a lack of clarity around the purpose of a variable or method. These variables and methods often end up as dumping grounds for unrelated bits of functionality. If you see a name like mydata or workmethod then you've probably found code without a clear purpose.
  • Obviously duplicated blocks of code are errors waiting to happen. When one of these blocks is modified, what happens to the other duplicated block?
  • Terse, impenetrable code is always going to be hard to maintain.
  • Methods or functions that are hundreds of lines long are usually a violation of the Single Responsibility Principle (discussed in Chapter 6), which is clearly a problem for maintenance.
  • Code comments that tell you nothing useful are a waste of space. The code will always tell you how something is done; code comments should tell you why.
  • Unexplained magic numbers are a major impediment to understanding code. For example, if a limit is $200 then at the very least you should create a const called something like OVERDRAFT_LIMIT. You should never use the bare number without an explanation, nor should you let a bare number appear in more than one place.
  • Every compiler warning is a potential bug in your code. You should fix those before they bite.
  • Code that serves no obvious purpose often indicates a lack of clarity in the design of the software.
  • Anything that is inconsistent with the rest of the code in the project is probably a mistake of some kind.
  • Long series of if-then-else statements or switch statements can often be expressed more clearly as a dictionary of lookup values.
  • Untestable code often results from a lack of structure and can indicate a violation of the SOLID principles.
8. Describe some things you always do when troubleshooting
Some programmers seem to have a knack for troubleshooting. When a customer reports a problem that can't be reproduced in the testing environment, these savant programmers will start twitching and blinking, and before you know it they have guessed the problem and are halfway done coding a solution.
The rest of us, unfortunately, need to work a bit harder.
What are some things that you always do (or try to do) when diagnosing a problem?
The first thing to do is try to get a clear understanding of the problem. All too often a bug report is vague or ambiguous. This is a common problem because most users are not trained in how to give good bug reports (nor should they necessarily be trained), and so they will make a bug report that reads like a medical complaint.
“I was trying to print the annual report, and when I click on the print button the screen shows red blotches and then nothing happens.”
If you're lucky the bug report will come from a professional tester or another programmer, and clarity won't be an issue.
After you understand the problem the next thing to try to do is reproduce the problem. Sometimes the problem can't be reproduced in the test or development environments and so you need to—very carefully, cognizant of possible bad outcomes—try to reproduce the problem in the production environment.
Reproducing the problem is often a major obstacle. It can be very difficult to do because sometimes the cause of a problem is something that you least expect. Sometimes the cause of a problem is two or more events coinciding. When you're trying to reproduce a problem, keeping alert, observing carefully, and keeping an open mind are vital. At this stage, you are trying to develop testable theories about why and how a problem occurs.
After the problem is reproducible (and sometimes you have to accept that a problem will occur infrequently) the next step is to isolate the source of the problem. This means trying to identify which parts of the system and which lines of code are involved. This, too, can be very difficult, especially when a problem is caused by the design of a system rather than any specific line of code.
To isolate a problem caused by poor coding, reverting to a known good state and working forward until the problem reappears can be helpful. After you know the point at which a problem appears, isolating the code that is potentially causing the problem is much easier.
Sometimes a problem is caused by a new kind of data, something the system has not previously handled. This can uncover latent bugs in code even when the code has not been changed. In large databases it can be very difficult to find the “new type” of data because most often you won't know what exactly you are looking for. Good logging can help; in fact, in all cases having a good logging system in place is helpful.
A good logging system is one that you can switch on when you need it and one where you can adjust the level of detail recorded in the log. A good logging system is one where you can look at the timing of events and where you have enough detail about each event that you can simulate (or even better, replay) the event in your development or test environment.
After you have identified the source of a problem you need to come up with a credible fix. This is sometimes the most dangerous part of troubleshooting, especially when a programmer is under pressure to get something fixed as soon as possible. The danger lies in making a mistake while rushing to fix the problem. If anything, this is the time when the programmer needs to slow down, consider the implications of the fix, have the fix reviewed by another programmer or tester, and then test the fix before releasing it to the live system. The last thing you want to do is make the problem worse or introduce another kind of problem that is worse than the original.
Here are some more ideas for effective troubleshooting. This is not a comprehensive list, and you should be able to think of more things that have worked for you in the past.
  • When a problem starts occurring you should ask, “What has changed?”
  • Keep in mind that events that occur close in time are not necessarily related. As they say, correlation does not imply causation.
  • Check most-likely causes before investigating less-likely causes.
  • Keep in mind that some bugs are caused by more than one problem.
  • Some bugs are caused by the precise timing of events.
  • Sometimes you won't be able to use sophisticated debugging tools, so don't forget about the humble print statement as a debugging tool.
  • Sometimes the problem will be outside your domain of expertise. If you need help to develop or test theories about a problem, you should not hesitate to involve other domain experts; for example, network or database administrators.
9. How do you go about getting familiar with a large code project?
Every time you change jobs a good chance exists that you will need to quickly become familiar with a large new code base.
How do you do that?
There is no single best way for a programmer to quickly become familiar with a large new code project. Here are some things that have worked for me, but your experience might be quite different:
  • One good way to learn a new code base is to have another programmer, someone who is familiar with it, give you a tour.
  • Every program has an entry point. Start a debugging session and see how the program sets itself up, which configuration files it reads, what database connections it establishes, which queries it runs, and so on. For most languages the entry point to an application is the main function. For static websites the default page is often default.htm or index.htm but it depends on how the site is configured. For ASP.NET applications usually some start-up code is in global.asax.
  • Start working with the code base by trying to implement a small feature, perhaps a simple bug fix.
  • Keep notes about what you find. My experience has been that I rarely refer back to these notes, but the act of writing down important things helps me remember them.
  • Pay particular attention to anything you don't immediately understand. It could be that the code base relies on a particular convention or an idiomatic style of coding. These “strange” things are often very significant for an application.
  • Don't forget that non-technical staff can also have good insight into how a system works.
  • A modern IDE (and even some text editors) will help you navigate a code base; for example, making it easy to jump back and forth between class and method definitions.
  • Treat any program documentation with suspicion. Look at when it was last updated.
  • Unit tests (assuming they pass) can be very helpful in understanding how functions are supposed to work, the arguments they accept, and what kind of setup is needed to make things work.
  • Bug-tracking software can give you an indication of typical problems found in the code base. This can give you some clues about its weaknesses and perhaps also its strengths.
  • If the application persists data and if the data has been modelled properly you might find important clues about the key entities of the system in the persistence later or the database itself. Foreign-key relationships can give you significant information about how data is structured (for example, “a purchase order is associated with either zero invoices or one invoice”). If the application uses an ORM then you should be able quickly find the key entities, and how they are used throughout the application.
  • Finally, don't think that you need to understand every little detail in order to work successfully with a new code base. You will need to treat some things as “black boxes,” accepting that they perform a function without necessarily understanding how they work. This will help keep you focused on the big picture.
10. Describe your understanding of cargo-cult programming
The phrase cargo-cult programming (or cargo-cult software engineering) is sometimes used to describe an undesirable approach to programming.
Describe what the term means and why this approach is undesirable.
Cargo cult refers to a number of South Pacific religions that appeared soon after World War II. The followers of these religions built mock-ups of airplanes and landing strips in the belief that by doing so they would summon the airplanes that had previously brought them cargo during the war.
The term cargo-cult programming is a reference to these religious practices, where a programmer ritualistically includes code that serves no real purpose. I have seen many examples of this, including:
  • Using the Hungarian naming convention without knowing what each prefix actually means
  • Using the DISTINCT keyword indiscriminately in SELECT statements
  • Writing a useless code comment above every line of code
  • Splitting a function into two for no reason except “to make it smaller”
This approach is undesirable for many reasons but mostly because it prevents the cargo-cult programmer from gaining a proper understanding of his code. The cargo-cult programmer who runs into trouble will be concerned with the appearance of his code rather than the functioning of his code. This is a serious problem because the cargo-cult programmer will then be stuck, relying on good luck to resolve the problem, and without the confidence that a problem is ever truly resolved.
The cargo cult approach to programming is similar to what Andrew Hunt and David Thomas (writing in The Pragmatic Programmer) refer to as Programming by Coincidence:

Fred doesn't know why the code is failing because he didn't know why it worked in the first place. It seemed to work, given the limited “testing” that Fred did, but that was just a coincidence.

Steve McConnell also calls out this style of programming in Code Complete, Second Edition:

Inefficient programmers tend to experiment randomly until they find a combination that seems to work.

11. Describe some potential downsides of code comments
When students are taught programming at school one of the first lessons they learn is that they should make sure to include appropriate code comments.
This is probably good advice, but what are some of the potential downsides of code comments?
Programmers spend a lot of their time maintaining code written by others. Code comments that are poorly written, incorrect, outdated, or misleading must rank as one of the top annoyances. After many bad experiences I no longer study code comments, and if there is one kind of comment I routinely ignore is it the kind you often see at the top of large source files in legacy code:
     /********************************************************
      *** FILE: Program.cs                                 ***
      *** DATE CREATED: Jan 13, 1968                       ***
      *** DATE LAST MODIFIED: Mar 31, 2001                 ***
      *** MODIFIED BY: Nigel                               ***
      *** PURPOSE: Facilitate the velocitous               ***
      ***          extramuralisation of the pendigestatory ***
      ***          interledicule                           ***
      *** MODIFIED BY: Nigel                               ***
      *** MODIFIED DATE: 1968-01-14                        ***
      *** MODIFIED REASON: Bug fix                         ***
      *** MODIFIED BY: Sandeep                             ***
      *** MODIFIED DATE: 1999-12-30                        ***
      *** MODIFIED REASON: Y2k bug fix                     ***
      ********************************************************/
I think eventually most programmers learn to ignore these comments, treating them as baggage to be towed around for the sake of appearances and eventually deleting them.
Another potential problem with comments is when they tell you nothing that isn't already obvious from the code.
 static void MakeDictionaryFromArray()
 {
     /* This method is commented poorly to make a point. 
        Don't imitate this style of commenting */
 
     // Declare an array of integers
     int[] array = { 1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 4 };
 
     // Declare a dictionary
     var dictionary = new Dictionary<int, int>();
 
     // For each integer in the array
     foreach (int i in array)
 
         // If the dictionary contains an integer...
         if (dictionary.ContainsKey(i))
 
             // Increment the dictionary value
             dictionary[i] += 1;
         else
 
             // Add this integer to the dictionary
             dictionary.Add(i, 1);
 }
Another kind of problem is when code comments are outright lies:
 static void MakeDictionaryFromArray()
 {
     /* This method resets a user password */
 
     // Establish a database connection
     int[] array = { 1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 4 };
 
     // Ensure we have an open connection
     var dictionary = new Dictionary<int, int>();
 
     // Reset the password
     foreach (int i in array)
         if (dictionary.ContainsKey(i))
             dictionary[i] += 1;
         else
             dictionary.Add(i, 1);
 
 }
Lies of the kind shown here are introduced when a programmer copies a function, modifies the code, and forgets to update the code comments. It can be very confusing, especially when the code comments are almost correct.
Inappropriate code comments are a form of code duplication and cause many of the same problems. Misleading and incorrect comments are worse than no comments at all.
12. When is it acceptable to produce lower-quality code?
Everyone agrees that programmers should strive to write good-quality code. Everyone might not agree on what this means, exactly, but in general all of us prefer to write good code rather than bad code.
Can you think of a valid, ethical reason why you would be motivated to write substandard code?
This is a question to which the answers are, I'm sorry to say, almost all controversial. In some ways an interviewer who asks this question is being unfair; it is a question with few satisfactory answers and many possible bad answers, all of them subjective.
On the one extreme are programmers who seem willing to spill blood over the suggestion they should ever compromise their ideals of software quality.
On the other extreme are programmers who intentionally produce poor-quality code, believing that “good” means “fast and cheap” no matter what chaos ensues in their code base.
There are also programmers who will do whatever their boss tells them to do, even if their boss has zero knowledge of software development and zero appreciation of how technical debt can cripple a business.
And don't forget the new programmers who are still forming their opinions about what good-quality code actually means. These programmers might not even be aware that a certain practice (for example, code duplication) is considered harmful by more skilled programmers.
The only sensible way to answer this question is to reframe it in a way that is more specific and, therefore, less likely to generate an argument; for example, “Can you think of a realistic situation in which you would knowingly compromise one of the SOLID principles?”
Notice that I've defined “substandard code” as being code that somehow compromises the SOLID principles. At the interview you would want to ask your interviewer if she can give you an example of what she means by “substandard.”
Let's pick on the principle of interface segregation (the “I” in SOLID) and make up an example of when you might willingly ignore this principle.
Immediately, one case comes to mind where I would be okay with not segregating my interfaces. If I were writing a disposable prototype system for the purpose of exploring requirements with a customer, then I might compromise interface segregation in order to get something up and running as fast as possible.
That said, I would stipulate several caveats:
I would have to know that this prototype was not going to become version 1.0 of the real thing. I would need to assure myself that this prototype would be destroyed after it had served its purpose.
I would also need to be unsure about the design of my interfaces. If I knew in advance, for example, that I needed an IFlippy interface and an IFlappy interface then I would not compromise the “I” in SOLID by mushing these two together. On the other hand, if the point of the exercise was to find out what my interfaces should be, and the prototype was going to help me do that, then I would have no problem with commingled interfaces.
To summarize, the best way to answer a loaded question like this is to reframe the question in a way that is specific and has less potential for violent disagreement. If possible you should encourage your interviewer to be more specific, and this will help you find an answer.
13. How can large code projects be kept under control?
Large software products always seem to contain a lot of regrettable code, and this is especially true for successful products that are maintained over many years. Almost everyone who has worked on a large code base will have issues with it in some way or another. It might be that it has too many or too few layers of indirection, that inconsistencies are throughout the code base, or any number of other problems.
As a programmer working on a large code project, how do you keep these kinds of problems under control?
This is one of those questions that if there were a simple answer the question would never need to be asked. If you could buy a tool or use a method that was guaranteed to keep a large code project under control then everyone would buy the tool and use the same method and the problem would disappear.
However, the reality is that large code projects almost inevitably slide into decrepitude. It's a function of their complexity, and there is little that one programmer can do about it except keep his own code as clean and consistent as possible.
Using tools to enforce things such as coding standards, naming conventions, and other stylistic aspects of code is sometimes helpful. To the extent that these tools work as advertised they are fine; it does pay to keep an eye on (for instance) cyclomatic complexity and (as another example) to ensure that all public methods are accompanied by code comments.
The trouble is that although measuring that (for example) a code comment exists is easy, measuring (automatically) that the code comments are actually helpful is quite difficult. I doubt very much whether any commercially available “code quality” tool will catch misleading code comments, identify poorly-named variables, or code that works but is needlessly complicated or difficult to read.
Ultimately, it is up to each individual programmer to actively resist the decline of a large code project. Passive resistance doesn't work.
14. How do you add features to unfamiliar code?
Describe how you would go about adding new features to a large, unfamiliar code project.
The biggest risk when working with a large, unfamiliar code project is the risk of breaking existing functionality. This problem is made worse when the code base has been allowed to deteriorate over the years. Let me give you an example of a deteriorated code base. I once worked on a large project that compiled with more than 30,000 compiler warnings. This project was full of duplicated functionality, quirky and inconsistent coding styles, circular project references (so you had to build it in two attempts), and many other tragi-comic problems that would have been funnier if it wasn't my responsibility to get new features working as quickly and error-free as possible.
A complete rewrite of a deteriorated, large code project is usually out of the question. Apart from anything else, when you consider how many hundreds of programmer-years have probably been sunk into that system you realize that throwing it away and starting again, despite the growing costs of maintaining it, is financially infeasible.
The idea of adding new features as a standalone application, perhaps integrated at the database layer but not sharing user-interface components, is usually shot down by users who won't like the idea of switching between different user interfaces in a supposedly integrated system. If you can successfully argue for this approach then perhaps this is the safest and most convenient way (for the programmer) to implement new functionality.
Assuming you have no choice but to add functionality directly into the existing code base then here are some tips on how you do it and survive the experience:
  • Seek to understand the small components that are used throughout the project.
  • Document (preferably in the code itself) how these components work and how to use them.
  • If you don't have unit tests for these components then you should consider writing some.
  • Start small and build up your understanding before tackling larger features.
  • Respect any existing conventions that have been consistently applied throughout the code. A poor coding convention consistently applied is better than the inconsistent application of a superior convention.
  • Resist the temptation to work on code that is unrelated to the new features you're supposed to be adding. No user will thank you for breaking an unrelated part of the system in the name of an improvement they can't see for themselves. Make a note for yourself and stay focused on the work you're supposed to be doing.
You should also read Working Effectively with Legacy Code by Michael Feathers. This is the go-to reference for, uh, working effectively with legacy code. There should be more books like this.
15. What exactly is wrong with so-called clever code?
Now and then every programmer will write a bit of code and feel justifiably proud of their achievement. This is part of the fun in programming. Sometimes, unfortunately, the pursuit of this happy feeling will lead programmers astray and cause us to write code that is, frankly, too clever.
What does it mean by code beingtoo clever,” and what exactly is wrong with clever code?
I should be clear that I am certainly not against cleverness per se, no; I like clever, I admire clever, I aspire to be clever. I enjoy writing and deciphering clever code as much as the next programmer. But I don't enjoy clever code quite as much when I'm in a hurry, or when I need to stay focused on an important job, or when I need to fix a critical bug, or when I'm refactoring, or adding a new feature, or…in fact, thinking on it, I almost always prefer to work with plain code.
What do I mean by clever code? Good question! Here's my stab at a definition:
Clever code (or more precisely, code that is too clever) is any code that unjustifiably slows down a competent maintenance programmer.
It is interesting that this definition could be applied to bad code as much as it applies to too-clever code. Do I really put them in the same category? In so far as they should be avoided, yes I do.
Notice also that I stipulate a competent maintenance programmer, and by this I mean a programmer who is familiar with the language and the framework of the application, and someone who is also familiar with the quirks and idioms that apply in this domain. Chapter 8 covers this subject in more detail. An incompetent maintenance programmer has bigger problems to worry about than dealing with clever code.
16. How do you improve your programming ability?
Supposing you wanted to improve your coding ability, how would you go about it?
Many people, including many programmers, think that the only way to improve a skill is to practice it. Practice makes perfect, they might say. Oh, and perhaps a bit of reading.
Practice, however, is useless if you are practicing bad habits. Practice is also useless if you don't understand what “improvement” looks like. For these reasons combining practice with the expert advice of a teacher or a mentor is always best. This doesn't always have to be in person; for example, you can get excellent advice from reading books and watching tutorials. One big advantage of a teacher (in person) is that she will be able to spot things that you won't necessarily see for yourself.
It is also true that unless you are challenged in some way, you will rarely improve beyond a certain level. To be sufficiently challenged you must accept the possibility of failing at something. Unless you risk failure, and occasionally fail, you are unlikely to improve a skill.
Programmers can also learn a lot from reading code written by others, and, in particular, looking closely at anything they don't understand.
Training courses can be useful, provided that you don't sit silently while an instructor reads from their presentation slides. Learning needs to be interactive; otherwise, the best you can hope for is to memorize some new facts. You don't get experience, the really valuable stuff, without trying it for yourself and learning from your mistakes.
17. Describe a coding project of which you're proud
Interviewers often ask about projects that made you proud. Talking about work-related projects is customary but most interviewers will let you talk about any programming project you've worked on, at work, at school, or even at home. The interviewer's aim is to give you a platform to display your enthusiasm for programming.
This is your opportunity to talk about what you enjoy, and what kind of circumstances or interests led to you doing a good job. You can also talk about a major learning experience, or how you survived a mission-impossible scenario. This is a platform on which you can and should have some fun.
If you are lucky enough to have had some time to spend on a pet software project then you should relish this question. I had some spare time, once, and I used it write several chess-playing engines. This is what I would talk about if given the chance at an interview. I would talk about my experimentation with the Minimax algorithm, and I would talk about how difficult it was to debug some of the bad moves my engine would play. I would talk about my first attempt (in Visual Basic no less!) at a chess-playing engine that was able to compute roughly 200 moves (“plies”) per second, and I would contrast this with the version I wrote later in Delphi that was about 100 times faster. I would talk about options for board representation and the various tricks I learned for generating moves with bitboards. I would talk about what I learned from studying the source code of the crafty chess engine. I would have a wonderful time reminiscing for as long as the interviewer would let me talk.
If you have never had this opportunity (and the reality is that some of us are swept along by circumstances that give us little or no spare time to play), then you will have to talk about an experience at work or school.
If this question scares you a little, just think about what you really enjoyed in a project. Don't worry if you enjoyed working on the graphic design of an application more than the programming, or if you enjoyed the database administration side of a project more than writing SQL. You are trying to show that you are excited by something, that your work has meaning to you, and that you find satisfaction in it.
If you are really stuck then I suggest you talk about a major learning experience that you've had. You could, for example, talk about a bug that you solved or helped to solve. What was hard about it? What did you overcome? What was the end result? What would you do differently?
And if you ever have the chance, I recommend trying to write a chess-playing engine. That way we will have lots to talk about at the interview.
18. Explain programming in non-technical terms
Suppose you are at a family event and your grandmother asks you to explain what it is that you do as aprogrammer.” What do you tell her?
If an interviewer has “good communication ability” on her list of “important qualities” then she might ask you a question like this.
Your answer should avoid using technical terms unless you can explain them simply. If you've never tried to explain programming in simple terms you can easily become tongue-tied as you realize halfway through a sentence about compilers that you haven't explained how machines are capable of following instructions, or how electricity can be manipulated to represent numbers, and in this tongue-tied state you might realize you haven't even thought to explain binary or logic gates.
Forget all that. What you need is a good analogy.
“Grandma, programming is like writing a knitting pattern. It's a list of instructions written so that someone can follow those instructions later to make a nice scarf or a pair of mittens.”
Of course, most grandmothers will at this point give you a stare that means you've taken the grandmotherly stereotype a bit far. Not all grandmas knit.
Still, this knitting analogy does convey the essential qualities of programming. As a programmer you are writing a list of instructions for later interpretation. The instructions will be taken literally so you can't assume that an unexpected turn of events will be handled gracefully. You need to consider what the knitter should do if the ball of wool runs out, or if the knitter has wooden needles instead of standard plastic knitting equipment. If you don't think of these possibilities and write instructions to handle them then your pattern (your program) might fail to produce the scarf or mittens for which it was written.
19. Explain the significance of coupling and cohesion.
You often hear these terms in debates about the implementation of an application. What is the programming-related meaning of the words coupling and cohesion, and why are they significant?
If you have just one class in your application and everything you write goes into that class then you probably have low cohesion. On the other hand, if you have classes that follow the single responsibility principle (the “S” in SOLID) then you probably have higher cohesion. Cohesion is a measure of how well the parts of a module (or the members of a class) fit together. It's just like that game on Sesame Street, the one where they sing one of these things is not like the other. If you have lots of things that are not like the other then you probably have low cohesion.
The main problem with low cohesion is that it works against the central aims of writing modular, reusable code. It makes reusing a module harder because you get all the unwanted baggage that comes with it. With low cohesion you also find that changes become more complex, requiring tweaks in more places.
Coupling refers to the degree of interdependence between classes. If you make changes to code in a highly coupled application you are more likely to cause unintended side-effects.
The main problem with highly coupled code is obvious. You don't want to be needlessly worried about unintended consequences when you change a line of code.
Low cohesion and high coupling often go hand in hand, and both indicate a lack of planning (or upkeep) in designing and implementing an application.
20. What is the real problem with global variables?
A global variable is a variable that is available throughout an entire application, regardless of where it is referenced.
That sounds quite handy, so what is the real problem with global variables?
In very small programs, say less than 100 lines of code, a global variable isn't such a big deal. The programmer can probably keep all these variables in her head while she writes code, and being able to reference the variable at any point and at any place in the program is probably quite useful.
After a program grows beyond a certain size (as most useful programs invariably do) then the problems of global variables become more obvious. Here are a few of the worst problems:
  • A global variable relies on the programmer remembering to set it as needed. They are implicitly present everywhere, so forgetting about them is easy.
  • If a program has more than one thread then these threads can come into conflict when both attempt to set and/or get the value of this variable around the same time.
  • Global variables make understanding code harder, because their existence must be either remembered or deduced and tracked down.
  • Global variables never fall out of scope, so they stick around and consume memory for as long as an application is running.
21. Explain the term technical debt in terms a non-technical manager will understand
Most programmers love the term technical debt because it is an apt metaphor for a problem they face every day. Unfortunately, the significance of the term appears to not be quite as apparent to as many non-technical managers.
Explain the meaning and significance of technical debt in terms a non-technical manager will understand.
The fact that many non-technical managers do not readily see the significance of the term technical debt is somewhat ironic, because the term was coined by Ward Cunningham in order to explain the problem to his boss, a non-technical manager. You can find transcript of Ward explaining this concept at http://c2.com/cgi/wiki?WardExplainsDebtMetaphor.
In financial terms, debt is accumulated when you borrow money, often for the purpose of meeting a short-term goal. The borrowed money helps you meet that goal but it must then be repaid, usually with interest.
Ward's “debt” was the increasing mismatch between the code his team had written and the “proper way to think about [their] financial objects.” Presumably at the time Ward coined this term he was trying to convince his boss that the team should spend time repaying the debt; that is, realigning their code to match their improved understanding, instead of continuing to add new features on top of the misaligned code.
Importantly, this is not a condemnation of the debt per se, but a reason to consider repaying the debt before accumulating more debt.
These days most programmers mean something a little different to what Ward meant when he first used the metaphor. When the term is used today it often refers to poorly-written code rather than code that is misaligned with a proper understanding of the domain.
In Ward's words:

A lot of bloggers at least have explained the debt metaphor and confused it, I think, with the idea that you could write code poorly with the intention of doing a good job later and thinking that that was the primary source of debt. I'm never in favor of writing code poorly, but I am in favor of writing code to reflect your current understanding of a problem even if that understanding is partial.

If the debt metaphor doesn't work with your non-technical manager you might consider an alternative. Perhaps your manager will find the metaphor of dirty dishes more persuasive.
Suppose every time you ate a meal you left the dishes in the sink. Eventually your sink will be filled with dirty dishes and you will have no clean dishes left in the house. Now every time you want to eat you first have to clean a dish. The accumulated dirty dishes slow you down, and they probably smell bad.
Rushing to write code, and presumably taking a few dirty shortcuts (like copying and pasting code, tweaking it to suit the new feature, and then releasing it in that state) is like avoiding doing the dishes after a meal. If you keep doing it then finding a clean dish to eat with becomes more difficult. Eventually, you have no choice but to buy more dishes, clean the dirty dishes, or go out to eat. I could compare “eating out” to the practice of outsourcing, but perhaps that would be stretching the metaphor a bit far.
22. Explain the term refactoring in terms that a non-technical manager will understand
The term refactoring is commonly used by many programmers and is generally understood to mean something positive. Explain in non-technical terms what the term means.
In the course of writing a program a programmer will routinely make many, many decisions about the detailed design of a program. In an ideal world, many of these decisions will have been considered long before the programmer started writing code but the reality is that writing code often reveals gaps in the analysis of a problem or in the design of a feature. These gaps are most often relatively minor; for example, choosing data types for a function, and the programmer will rely on judgement and experience to make good decisions. If the programmer were to pause for discussion at every decision point then progress would be painfully slow.
Programmers usually make the right decisions as they write code, but not always. Suppose a programmer decides to use a string data type to hold a date value, perhaps thinking that values like “tomorrow” and “five weeks from now” should be allowable in addition to proper dates such as “2001-01-01” and “2029-04-07.”
The programmer who makes a decision like this might come to regret it, perhaps finding that a lot of time and effort is now being spent explaining why “tomorrow” never comes. The programmer might now want to refactor the code so that all date values entered into the system conform to a pattern of “YYYY-MM-DD.” This is not a bug-fix per se, because the decision to permit unusual date values was deliberate, and the program is running according to the programmer's design.
The example of a poorly chosen data type is one where the end user will see evidence of the problem and will, therefore, readily agree that something needs fixing. Persuading a product owner that something needs fixing isn't hard to do when it is readily apparent that something is broken.
Programmers know that many things make the upkeep of a program more difficult than it should be, but that the end user will never see any of these things directly. If you find duplicated code you know that this can cause many kinds of problems but justifying the required time to fix it can be difficult when the end user sees no difference at all in the fixed product.
Refactoring is the process of fixing these internal problems without changing the external behavior of an application.
When trying to persuade a product owner that time should be spent fixing these internal problems, Andrew Hunt and David Thomas, writing The Pragmatic Programmer suggest that you use a medical analogy:

…think of the code that needs refactoring as a “growth.” Removing it requires invasive surgery. You can go in now, and take it out while it is still small. Or, you could wait while it grows and spreads—but removing it then will be both more expensive and more dangerous. Wait even longer, and you may lose the patient entirely.

23. What is the significance of a leaky abstraction?
In 2002 Joel Spolsky wrote a blog post entitled The Law of Leaky Abstractions:
The term leaky abstraction has now entered the mainstream of programmer jargon.
Explain the meaning and significance of this term.
If you look past the façade of a high-level programming language such as C or Java to the native machine code generated by a compiler (or a runtime platform) you can see that the high-level language is an abstraction. The high-level programming language enables programmers to write instructions for a computer with less effort than if they were using a low-level language. The programmer using a high-level language is less concerned with implementation details, and clearly this is very convenient.
Programmers are accustomed to taking advantage of all kinds of abstractions, each providing a convenient façade of simplicity. You are so comfortable with abstractions, including their occasional imperfections, that you sometimes forget that non-programmers depend on these abstractions even more heavily than you do. When an abstraction fails to hide the underlying complexity it can be paralyzing for the end user. An abstraction that exposes the complexity is sometimes said to have leaked.
This has important implications for how you design applications. Suppose you write an application that communicates with a remote server to retrieve, say, market data. You know that the component you use for communication with the server is reasonably reliable, but you also know that it depends on communication over a network and is therefore vulnerable to network outages. When writing code to deal with an error returned by this component you have a choice. You can expose the underlying error message to the end user, or you can dress it up in some way, or you might decide to temporarily hide the problem and reveal it later only after you have tried and failed a number of times.
In effect, you must decide how to handle the stuff that leaks out of the abstraction, and you should try to ensure that your own layer of abstraction remains consistent from the perspective of the end user—anything less sets up your users for some serious disappointment.
24. What is continuous integration and how is it helpful?
The concept of continuous integration has progressed to the point where you can buy specialized tools to support the practice.
What does the term continuous integration mean, and how is it helpful?
In the (heavily stereotyped) bad old days of programming, individual programmers would work in isolation for periods of time before sharing their efforts (the code they produced) with the rest of the team. This would often result in serious delays and awkward problems caused by a mismatch of expectations and incompatibility between the code submissions of these developers.
Consequently, integrating these individual contributions as often as practically possible is now generally accepted as good practice. Developers following the practice of continuous integration are therefore encouraged to share their work-in-progress, and to accept the work-in-progress of other developers, thus minimizing the potential divergence of ideas and coded implementations within the team.
One of the immediate problems faced by teams practicing continuous integration is that they are often derailed by a submission of faulty code. If one developer “breaks the build” then all developers who have accepted this code into their working copy will have to either work on the problem or wait until another developer fixes it.
The problem of sharing faulty code is addressed by software that performs frequent automated builds. These builds are performed either periodically or whenever a developer commits code to the shared code repository. If the build fails, then developers in the team are notified that the most recent code submission is faulty and should be avoided.
When the automated build system informs the developers that the code has been fixed then they are again free to integrate the latest changes from other developers in the team.
The concept of build failure originally meant simply that the code would not compile, but today it means much more:
  • Unit tests are run as part of a build, and if any of these tests fail then the build itself is considered to have failed.
  • Coding conventions are checked by automated tools during the build, and if code is found to not follow these conventions then the build fails.
  • Code metrics are checked by automated tools, and if these metrics are not within acceptable levels then the build fails.
  • Documentation can be generated directly from the code, and if it cannot (for example, a public method is not commented) then the build fails.
25. What is your favorite software development methodology?
once worked with a very plain-speaking programmer who snorted loudly when I asked a candidate this question at an interview. He explained afterwards that he thought the question was trendy and meaningless, and that the word methodology was a pretentious substitute for the word method. He was probably right, so here is the same question asked in a more plain-speaking way:
In your experience, what methods most improve the effectiveness of a software development team?
Despite my attempt at plain-speaking, this is still a loaded question. Every unqualified answer to this question is bound to be wrong, more or less. A good answer depends on:
  • The people in the team and their experience and skills
  • The work the team is doing
  • The definition of effective
As with most vague questions, and certainly with loaded questions, the best way to provide a sound answer is to define a limited context and answer within the bounds of that context. It is unfair but an interviewer who asks a vague, hand-wavy kind of question will probably also be disapproving of a vague, hand-wavy answer.
Suppose the team was producing a lot of code that later failed in testing. You could define effective in that context as “failing fewer tests.” Now, the question is much easier to answer. You can probably think of lots of good techniques for failing fewer tests. Here are three to get you started:
  • Have testers write tests at the same time that developers are building the system, and have both developers and testers base their work off a common, shared specification. Developers should review these tests to help the team gain a common understanding of the specification.
  • The development team, if it isn't already, should be structuring their code so that it is easy to test. This generally means following good practices such as the SOLID principles, in particular the principles of Single Responsibility and Interface Segregation.
  • Unit testing should be commonplace, and unit tests should be run as part of a continuous integration (CI) build.
Suppose the team was struggling to get requirements correct. You can probably think of lots of things that might help; here is another short list to stimulate your own ideas:
  • Requirements come from customers and the product owner. Be sure that the team is talking to the right people.
  • Communication is generally best when a short feedback loop exists, or to phrase it in the negative: One-way communication is the least effective form of communication. Communication between the development team and the product owner should be interactive, frequent, and on-going.
  • Detailed specification documents can be useful, but focusing on communicating the why rather than the how can be more useful.
  • Anything that is written down needs to be written well.
26. How do I tell the product owner that his requirements are ridiculous?
Programmers are usually not backward in regard to assessing the merits of a requirement that appears to be unrealistic or impossible.
How would you explain to a product owner that you cannot implement his requirements?
An interviewer who asks you this will have several possible motivations for asking:
  • He wants to understand how you might handle situations of conflict.
  • He wants to see how you react to difficult or seemingly impossible requirements.
  • He knows that you will be working with a difficult product owner.
When dealing with a difficult requirement the key thing to remember is that you are the programming expert, and in all likelihood the product owner is relying on you to help him find a good balance between what he would like and what can realistically be achieved. You can't, for example, take a newly formed and inexperienced team and in six months have it produce a search box that works “just like Google.” What you can do is strive to understand the vision of the product owner and to define a realistic implementation of that vision, working with the product owner (not against him) to agree an implementation that is appropriate for your situation.
Let's take this example and break it down. “A search box just like Google” (or Bing, or Yahoo!) could mean a lot of different things:
  • Faster than anything comparable
  • Comprehensive
  • Visually minimalistic
  • Authoritative
  • Scalable
  • A platform for advertising
  • Applying heuristics to find good results
  • Ranking results with a secret algorithm
If you list out the possibilities like this and discuss them with the product owner then the job becomes a bit easier, maybe. Unfortunately, sometimes you will get a “yes, please” to all of them rather than a sensible rationalization of priorities.
Here are some generally applicable tips for exploring a seemingly impossible requirement:
  • Stick to the facts. If your team (or you) have no idea how you would implement a requirement then say so. If the technology isn't capable of meeting a requirement then explain why this is the case.
  • Estimate the probable development time for the requirement. Sometimes a cost-based argument is enough to lower the intensity of a debate and bring it back to reality. You might need to spend some time obtaining a credible estimate.
  • Explore the expected value of the requirement. Is it to protect or generate revenue? Is it expected to provide a competitive advantage? Is it a unique or ground-breaking feature? When you look closely at a requirement you will often see that the underlying expectation is more easily satisfied with an alternative.
  • You can often get to the underlying assumptions or expectations by politely and respectfully persisting with the question, “Why?”
27. What advice do you have for new programmers?
Suppose you were given the responsibility of mentoring a new and inexperienced programmer. What key bits of advice would you want to ensure this programmer understands as she embarks on her programming career?
Everyone is going to have his own ideas about this based on their own unique experiences. Here are some of mine:
  • Writing code is an act of communication. Obviously you are instructing a machine but perhaps more important is the communication between you and the maintenance programmer who reads your code at a later time. That maintenance programmer might be you, or it might be a person from another culture altogether.
  • Writing as clearly as you can should always be a priority. This applies to everything you write, not just code.
  • Get involved in a programming communities both online and (if you can) also in the real world. You will learn a lot and make new friends. There is no downside.
  • Study code you don't understand; it's a great way to learn new tricks.
  • A lot of the fun in programming is learning new things. Keep a list of things you want to learn about in more detail and return to this list whenever you have spare time or when you get bored.
  • Learn complimentary skills; in particular work on your writing, speaking, and presentation skills. You will never regret improving these.
  • One of the most important but most overlooked measurements of code quality is customer satisfaction.
28. Do coding standards influence code quality?
Do you think raising the quality of code by enforcing coding standards is possible?
More often than not if you ask developers what things influence the quality of code their first answer will be “the quality of the developer,” by which they mean the skill and experience and judgement of that developer.
If you press developers hard enough they will, eventually, admit that coding standards have a part to play.
The trouble with coding standards is that they are perceived as suppressing individual creativity and freedom of expression. That might be true to some extent but a key benefit of adherence to standards is improved consistency and therefore improved readability of code. Readability is a key factor in the ease with which code can be maintained. Code that is hard to read is always harder to maintain.
The alternative (that is, no standards) is that the developer will need additional time adjust to the individual styles of each developer as they move through a code base.
29. Which coding standards are the most important?
Every software development team seems to have a set of coding standards. Describe some coding standards that you think are important for a team striving to produce good-quality code.
This question presupposes that coding standards do influence code quality, and you can assume that by “code quality” the interviewer means consistency and readability. A team without coding standards might follow whatever seems fashionable at the time, and their code base will potentially suffer from individual idiomatic coding styles that harm readability.
After a team has agreed to follow a set of standards, here are some key standards that should probably appear at the top of its list. Notice that this list does not define what these standards should be. This is a list of standards that are important enough that the team should think about and agree to upfront.
  • Encouraged idioms
  • Discouraged idioms
  • Naming conventions (yes, really, names are important!)
  • Code-comment style guidelines
  • Exception-handling style
  • Use of assertions
30. Why is the number of lines of code produced by a programmer a poor measurement of programmer productivity?
Every now and then it will occur to a non-technical manager that measuring the cost-effectiveness of a software development team ought to be possible. This manager might do some reading about the practice of programming and it will occur to him that because programmers write code, just like authors write books and lawyers write contracts, then deriving some useful information by counting the number of lines of code produced by a programmer over time should be possible.
Why is lines of code (LoC) a poor measurement of programmer productivity?
This is a question that just won't go away. Every programmer should be ready with the following answers:
  • A skilled programmer is likely to improve code by removing lines rather than adding them. Clear code is partly a function of how many lines of code must be read, and so removing lines of code while retaining clarity and functionality is worthwhile.
  • The corollary of the first point is that an unskilled programmer is more likely to add unnecessary lines of code (see also question 10).
  • LoC is a measurement that is easy to artificially inflate. After a programmer is aware that “productivity” is measured by lines of code then he or she tends to optimize for that measurement.
  • The number of lines in a body of code does not often correlate to the difficulty or producing those lines. An application's “secret sauce” (the thing that gives it a competitive advantage or a unique ability) is unlikely to be the largest section of code. More often it is the code generated automatically by the form designer of an IDE that contains the most lines.
LoC is a measurement of quantity not quality, and it is therefore inappropriate to use as a measurement of programmer productivity. Deriving any meaningful conclusion from counting lines of code is difficult if not impossible.
31. Is the goto statement really that harmful?
Most programmers have been instilled with a strong sense of certainty that the goto statement is a source of untold misery and that it must be avoided at all costs.
Explain why the goto statement is considered harmful.
This bit of programming lore is handed down to every new generation of programmers, usually accompanied by a warning that using goto leads to spaghetti code and unstructured code and all manner of foul outcomes.
The origin of this lore is not hard to find. The idea that the goto statement is harmful was popularized by Edsger Dijkstra in in a letter published by the Association for Computing Machinery (ACM) in 1968. In this letter Dijkstra writes:

The goto statement as it stands is just too primitive; it is too much an invitation to make a mess of one's program.

To understand this criticism you need to understand what Dijkstra means by “too primitive.”
You know that all programming languages have constructs for controlling the flow of the program. You know about the for loop, the while loop, and if you are lucky you know about the try-finally construct.
What would happen if all of these constructs were taken away and in their place were left just a goto and an if statement?
Well, obviously there would be a lot of angry programmers, but after the dust had settled then these programmers would get back to work and the first thing they would do would be to reinvent all of these control constructs using if and goto!
The point is that the goto statement is a foundational building block and, therefore, banning it is just as foolish as using it when better control structures are available.
Without goto you would not be able to construct the more advanced flow-control structures. Let's respect it for what it is and not ban something so useful because programmers sometimes write poor code.
32. Should software managers have technical backgrounds?
Should software development managers have technical backgrounds? To ask this another way, can a software development manager without a technical background be effective? (Notice that no stipulation exists that this technical background is necessarily programming experience.)
I can only answer this from my own experience, because I don't have an industry-wide study on the effectiveness of non-technical development managers. I also note that this question tends to divide the programming community, with both sides telling stories of the best manager they ever had who was, of course, “technical” or “non-technical.”
From my own experience my answer is, “All other things being equal then, yes, an effective manager should have a technical background” and here are some reasons why:
  • An effective manager needs to understand technical issues without being spoon-fed.
  • The manager is an ambassador for the team and will speak on the team's behalf. A non-technical manager will struggle to do that effectively and consistently.
  • An effective manager must trust the team but be capable of double-checking facts when the need arises.
You might suppose that for all of these points the non-technical manager can delegate responsibility to a technical person, and you would, of course, be correct. The question then becomes which manager is more effective: the one who can choose to delegate or the one who must delegate?
I look forward to hearing your views on this one.
33. Should application architects know how to write code?
Similar to the question about technical and non-technical managers, another question I often hear is whether application architects should be capable of writing code. What is your opinion?
Perhaps to a greater extent than the development manager, the application architect should be capable of writing code. That is not to say that the architect should spend all (or even a significant portion) of her time coding, but rather that the architect should be capable of writing a prototype to demonstrate her ideas. The architect must also be capable of understanding alternatives and must be able to hold meaningful and in-depth discussions with the developers who will implement the designs and plans laid down by the architect. There's nothing worse (for the programmer) than an architect who has lost touch with reality. As an application architect, keeping your feet on the ground and your head in the clouds is part of the job.
34. What is your desert-island “best practice”?
If you could choose one so-called “best practice” that everyone in your team (or, if you prefer, the world) would instantly start following, what would it be?
I suspect that by asking this question I have entered a depth of subjectivity from which I might not emerge unscathed. Every programmer with experience will have a first-hand account of how a particular approach has revolutionized the way they do software development. I won't persuade these programmers (nor should I try to persuade them) that my view is better than theirs. What I will say is that over the decades many approaches have been hailed as revolutionary, and I don't think the story of programming has yet been concluded.
At the time of writing this book some of the most popular approaches include:
  • Unit testing and test-driven development
  • Agile
  • Functional programming
Looking back further in time you see:
  • CASE tools and so-called 4GLs
  • Object-oriented programming
  • Client-server and n-tier architecture
  • RAD and JAD
Looking back even further you see:
  • The approach that is now known as “waterfall”
  • Procedural and modular programming
So, considering all of this history and the fanfare that accompanied each of these approaches, which “best practice” would I magically ensure that every programmer follows?
That's easy: If you are a programmer then with the power of my magic wand I decree that from now on you must think for yourself.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.141.30.211