Top 20 Things a Programmer Should Know
I applied the 80/20 principle to the 97 Things a Programmer Should Know book. I have eliminated things that are too obvious or trivial to me. Some of the tips will make sense only if you get the context by reading the book. This is a good book, buy it. There were 16 more that was almost good enough to be in this list but did not make it. Here is my top 20:
1. Apply Functional Programming Principles
Referential transparency is a very desirable property. It implies that functions consistently yield the same results given the same input, irrespective of where and why they are invoked. That is, function evaluation does not depend on the side effects of mutable state.
What would the user do?.
You are not the user. We all tend to assume that others think like us. This is not true. Spending an hour watching users is more informative than spending a day guessing what they want.
3. Your customers do not mean what they say.
Discuss topics numerous times with your customers before you decide that you understand their needs. Try restating the problem two or three times. Talk to them about the things that happen just before or just after the topic you’re talking about to get better context. If at all possible, have multiple people tell you about the same topic in separate conversations. You will hear different stories, which will uncover separate yet related facts. Two people telling you about the same topic will often contradict each other. Your best chance for success is to hash out the differences before you start development.
Use visual aids in your conversation. You could use whiteboard, visual mockup early in the design phase or a complex functional prototype. Visual aids lengthen attention span and increases the retention rate of the information.
4. Start with Why
Instead of starting with a yes or no, start with why. Find the reason behind the request. Usually when you know the context of the request, new possibilities open up. It’s common for the request to be accomplished with the existing product in some other way, allowing you to say yes with no work.
Sometimes their idea might be incompatible with your view of the product. Turn that ‘Why?’ on yourself. Sometimes the act of voicing the reason will make it clear that your first reaction doesn’t make sense. If not, bring in other key decision makers. Remember, the goal of all of this is to say yes to the other person and try to make it work, not just for him but for you and your team as well.
If you explain why the feature request is incompatible with the existing product, then you can have a productive conversation about whether you are building the right product.
5. Hard work does not pay off.
You are working too hard if you are trying to be focused and productive for more than 30 hours a week. Reduce your workload to become more effective and get more done. This is a direct consequence of the fact that programming and software development involve a continuous learning process. As you work on a project, you will understand more of the problem domain and find effective ways of reaching the goal. To avoid wasted work, you must allow time to observe the effects of what you are doing, reflect on the things that you see and change your behavior accordingly.
Most software projects are like a long orienteering marathon. In the dark, with a sketchy map as guidance. If you set off in one direction, running as fast as you can, you might impress some, but you are not likely to succeed. You need to keep a sustainable pace, and you need to adjust the course when you learn more about where you are and where you are heading.
Learn more about software development and programming techniques. Read books, go to conferences, communicate with other professionals, experiment with new implementation techniques and learn about powerful tools that simplify your job. Keep yourself updated in your field of expertise. Preparation and education are an essential part of our profession.
Be focused on the project, contribute as much as you can by finding smart solutions, improve your skills, reflect on what you are doing and adapt your behavior. Being focused and productive 60 hours a week is not a sensible thing to do. Prepare, effect, observe, reflect and change.
6. Do lots of deliberate practice.
You do deliberate practice to improve your ability to perform a task. It’s about skill and technique. Deliberate practice means repetition. It means performing the task with the aim of increasing your mastery of one or more aspects of the task. It means repeating the repetition. Slowly, over and over again, until you achieve your desired level of mastery. You do deliberate practice to master the task, not to complete the task. The principal aim of deliberate practice is to improve your performance.
The key to developing expertise is deliberate practice: not just doing it again and again, but challenging yourself with a task that is just beyond your current ability, trying it, analyzing your performance while and after doing it and correcting any mistakes.
7. Reinvent the wheel often.
The same way that watching a movie about sailing is very different from going sailing, so too is using existing code versus designing your own software from the ground up, testing it, breaking it, repairing it and improving it along the way. Reinventing the wheel is about how to get an intimate knowledge of the inner workings of various components that already exist.
Most developers have never created core software libraries and therefore do not have an intimate knowledge of how they work. The consequence is that all these kinds of software are viewed as mysterious black boxes that just work. Understanding only the surface of the water is not enough to reveal the hidden dangers beneath. Not knowing the deeper things in software development will limit your ability to create stellar work.
Reinventing the wheel and getting it wrong is more valuable than nailing it first time. There are lessons learned from trial and error that have an emotional component to them that reading a technical book alone just cannot deliver.
Learned facts and book smarts are crucial, but becoming a great programmer is as much about acquiring experience as it is about collecting facts. Reinventing the wheel is as important to a developer’s education and skill as weightlifting is to a body builder.
8. Continuous learning.
Read. Join mailing list. Get hands on and write code. Find a mentor and work. Get to know the frameworks and libraries you use. When you make a mistake, fix a bug or run into a problem, understand what happened. Teaching is a good way to learn. Participate in lunch-and-learn, user group or local conference. Join or start a study group. Go to conferences or watch the talks online. Learn a new language. Branching out gives you new ideas you can use in your current technology stack. Learn about your domain. It helps you to understand the requirements and solve business problems.
9. Know how to use command line tools.
The search and replace capabilities provided by the grep and sed utilities are often more powerful than IDEs.
To find classes with same name:
find . -name ‘*.rb’ | see ’s/.*\///‘ | sort | uniq -c | grep -v “^ *1” | sort -r
10. The Unix tools are your friends.
It’s very easy to extend the world of the Unix tools. Just write a program in any language that plays by a few simple rules:
- Your program should perform just a single task
- It should read data as text lines from its standard input
- It should display its results unadorned by headers and other noise on its standard output
Parameters affecting the tool’s operation are given in the command line.
11. Step back and automate, automate, automate.
You can go a long way with a shell language such as bash or PowerShell and a build automation system. If you need to interact with websites, use a tool such as iMacros or Selenium.
You don’t have to learn all of bash to get started. Learn as you go. When you have a task that you think can be automated, learn just enough about your tools to do it. And do it early in a project when time is usually easier to find.
12. Put everything under version control.
Tag a software release with a symbolic name so that you can easily revisit in the future the exact version of the software your customer runs. You can create branches of parallel development: most projects have an active development branch and one or more maintenance branches for released versions that are actively supported.
Commit each logical change in a separate operation. Lumping many changes together in a single commit will make it difficult to disentangle them in the future. This is especially important when you make project-wide refactorings or style changes, which can easily obscure other modifications.
13. Put the mouse down and step away from the keyboard.
While coding, the logical part of your brain is active and the creative side is shut out. It can’t present anything to you until the logical side takes a break.
Next time you hit a nasty problem, do yourself a favor. Once you really understand the problem, go do something involving the creative side of your brain - sketch out the problem, listen to music or go for a walk. Sometimes the best thing you can do to solve a problem is to put the mouse down and step away from the keyboard.
14. Missing opportunities for polymorphism.
Polymorphism creates tiny localized execution contexts that let us work without the need for if-else blocks. Being in a context allows us to do the right thing directly, whereas being outside of that context forces us to reconstruct it so that we can then do the right thing. With careful use of alternate implementations, we can capture context that can help us produce less code that is more readable. Command and Double Dispatch play well together. By applying polymorphism, we reduce the if-else blocks in our code.
15. Prefer domain-specific types to primitive types.
The code becomes more readable, as it expresses concepts of a domain, not just Float or String. The code becomes more testable, as the code encapsulates behavior that is easily testable. The code facilities reuse across applications and systems. Start exploring domain-specific types for the purpose of developing quality software.
16. Test for required behavior, not incidental behavior.
A common pitfall in testing is to hardwire tests to the specifics of an implementation, where those specifics are incidental and have no bearing on the desired functionality.
When tests are hardwired to implementation incidentals, changes to the implementation that are actually compatible with the required behavior may cause tests to fail, leading to false positives.
To be effective, tests need to state contractual obligations rather than parrot implementations. They need to take a blackbox view of the units under test, sketching out the interface contracts in executable form. Therefore, align tested behavior with required behavior.
17. Test precisely and concretely.
In specifying behavior, tests should not simply be accurate: they must also be precise. The result of adding an item to an empty collection is not simply that it is not empty: it is that the collection now has a single item and that the single item held is the item added. Two or more items would qualify as not empty and would also be wrong. A single item of a different value would also be wrong.
18. The golden rule of API design.
It’s not enough to write tests for an API you develop; you have to write unit tests for code that uses your API. When you follow this rule, you learn firsthand the hurdles that your users will have to overcome when they test their code independently.
19. Write Tests for People
Good tests act as documentation for the code they are testing. They describe how the code works. For each usage scenario, the tests:
- Describe the context, starting point or preconditions that must be satisfied
- Illustrate how the software is invoked.
- Describe the expected results or post conditions to be verified
Different usage scenarios will have slightly different versions of each of these. Others should be able to look at a few tests and by comparing these three parts of the tests, be able to see what causes the software to behave differently. Each test should clearly illustrate the cause-and-effect relationship among these three parts.
Give each test a meaningful name that describes the particular usage scenario. It is a good idea to test your tests. You can verify that they detect the errors you think they detect by inserting those errors into the production code. Make sure they report errors in a meaningful way. You should also verify that your tests speak clearly to a person trying to understand your code. The only way to do this is to have someone who isn’t familiar with your code read your tests and tell you what they learned.
20. One binary rule.
Build a single binary that you can identify and promote through all the stages in the release pipeline. Hold environment-specific details in the environment. This could mean, keeping them in a file or in the path. Keep the environment information versioned too. If the environment configuration breaks, without versioning we cannot figure out what changed. The environmental information should be versioned separately from the code, since they’ll change at different rates and for different reasons.