This week I’ll be covering a topic in the engineering field: the non-technical aspect of the technical field. As this is a topic that’s rarely covered (that doesn’t involve everyday soft skills such as communication and time management), I’m excited to share my review of this exclusive book.
Verdict: A non-technical, technical book in software engineering; rarely done let alone rarely done well, but this book does it. Best audience is for professionals who have just started out in their career.
Having worked in the industry for a year now, I feel like now or a few months earlier would have been the perfect time to tackle this book. Andrew Hunt covers many topics that may be – or should be – commonplace to the experienced engineer, including orthogonality, design by contract, and software entropy. Despite the topics being rather introductory, I do see most of my coworkers breaking such practices, even those with over a decade of experience, so there is definitely merit in skimming through this book no matter your skill level.
In addition, despite the topics being introductory, I would suggest against reading this book if you are not yet in the work force (which includes internships). I feel that personally, having my current project in mind when reading each principle gave me a concrete image of these abstract ideas, making them easier to remember.
This book may leave you thinking “I already know all of this!” Everything is rather familiar. However, even though you may know it all, do you actually follow all the practices regularly? Most likely the answer is no, but EVEN IF the answer were yes, reading this book helps you as an experienced programmer gain perspective (see my earlier review Made to Stick and the Curse of Knowledge) of those newer to the industry. Use your knowledge to help those around you!
As usual, I’ll break up the contents by their chapters, briefly explaining the concepts from each section.
A Pragmatic Philosophy
Hunt’s definition of a “pragmatic programmer” is actually rather simple and self-explanatory, really, the things you hear everyday even outside the engineering world. Regardless, before anything else, a pragmatic programmer shall keep the following in mind above all else:
- Take responsibility for your code. This isn’t high school.
- Software entropy: Don’t neglect bad decisions (code, design, etc); stay on top of everything. The later you tackle problems, the faster they build up and the more difficult it becomes to clean things up.
- Be a catalyst for change. Set an example.
- Don’t just focus on your task; remember the big picture.
- Always be learning. Diversify your “knowledge portfolio.” At work, take advantage of the project and work environment as much as possible. Outside of work, invest in reading books (both technical and non-technical), learn a new computer language etc.
- Communication is, as always, key.
A Pragmatic Approach
Hunt now starts diving into the code by introducing several principles to keep in mind:
- DRY principle: Don’t Repeat Yourself.
- Orthogonality: Keep sections independent and decoupled (Hunt doesn’t mention this, but essentially the S in the SOLID principle). This doesn’t apply to only code.
- Code with reversibility and flexibility in mind.
- Iterate quickly and often.
- Prototype to learn.
- Learn how to estimate; iterate on estimating with the code.
The Basic Tools
Many beginners overlook the power of the tools provided to them. IDEs, debuggers, etc. Hunt enlightens the reader with the benefits of investing a little time for learning tools to increase their productivity tenfold:
- Learn the strengths of using plain text such as XML.
- Learn the strengths of the command shell.
- Specialize in a single editor and learn as much as you can about it.
- Always use source control. Not only in code.
- Learn how to debug: fix the problem, not the blame. Be able to explain the problem to someone else – “rubber ducking.” Don’t assume anything – prove everything.
- Learn the strengths of text manipulation languages.
- Write code that writes code.
Hunt extensively preaches to code defensively. Code in defense against your own mistakes. Assume that the next person reading your code – even if it’s yourself – knows nothing.
- Design with contracts: understand preconditions, postconditions, and class invariants, and design them before you tackle the code.
- If the program breaks the contract, make the program crash early. The later the program crashes, the more potential bugs you may need to sift through, and the more the program may be working in an invalid state.
- Use exceptions for exceptional cases. Remember that exceptions are expensive.
- Finish what you start. Remember to deallocate when necessary.
Bend, or Break
The project must be as flexible as possible, or else it won’t survive in the long run. Hunt explains concepts on just how to do this, extending upon his concept of orthogonality and decoupled code from “A Pragmatic Approach”:
- Make as much of the program configurable as you can, from screen sizes to algorithms.
- Put abstractions in code. Keep details in metadata. Metadata keeps the program flexible.
- Always design for concurrency. Keep linear thinking to a minimum, and keep in mind how to maximize parallelism.
- Separate views (the displaying of data) from models (the data itself).
While You Are Coding
This section finally dives directly into code implementation, whereas all the previous sections were more abstract ideas to take into consideration:
- Recognize when a program is implemented by coincidence. Program deliberately (with an understanding of all requirements, with a full design in mind, with no assumptions – but if you do make assumptions, document and test them).
- Understand the basics of Big-O notation. Estimations will take you far.
- Don’t hesitate to refactor. Refactor early, and refactor often. If any of the principles in this book are violated, refactor. If something is out of date, refactor. Keep software entropy in mind. Refactoring is necessary since requirements and designs are never static.
- Don’t refactor and add functionality at the same time.
- Ensure you have good tests and run tests often before refactoring.
- Make code easy to test. Keeping the bullet point design with contracts in “Pragmatic Paranoia” in mind. Design to test.
- Never use code you don’t understand. It will be difficult to debug and maintain if you don’t. If it’s difficult to understand it takes time for you to understand it, take the time to understand it, then refactor if possible.
Before the Project
What you do outside of code, before you code, is at least just as important as coding itself. Hunt emphasizes the need for taking care of prerequisites with the bullets below:
- Seek out requirements. Don’t just let them come to you; deliberately gather as many requirements as possible before you start designing, let alone implementing.
- Work with a user to think like a user.
- Keep requirements abstract. Abstract requirements are more extensible and understandable to implement.
- Maintain a glossary for your project. This glossary could be used for both front-end and back-end developers for a single source of truth.
How are “pragmatic” projects run? Hunt’s tips on making teams run as smoothly as possible:
- Organize around functionality, not job roles. Remember that functionality does not necessarily mean end-user use cases.
- Automate as many mindless tasks as possible. Manual procedures are slow and error-prone.
- Test early, test often, test automatically. Test the tests. Once again, keep in mind software entropy.
- Test for state coverage, not code coverage. Just because every line of code is covered in tests does not mean every possible state is. Unfortunately, no tool can determine every state for you, so this is where designing with contracts comes into play. Design those contracts as aggressively as possible.
- Find bugs once. Time spent on finding the same bug multiple time is time wasted. Document found bugs, fix the code, fix the unit tests, fix everything to ensure that this bug will not appear anymore.
- Build documentation with your code, don’t just use documentation as an add-on. Keep all of these programming principles in mind when it comes to documentation. Treat documentation with equal care.
- Sign your work. Anonymity can lead to sloppiness because nobody will know you cased the problem. Be proud of the work you do, and take responsibility for the work you do.
So despite the topics of this book being so introductory, why a full five stars? Simple: How many other books are like this, let alone do it so well? Even with my lack of experience and my initial judgment saying that this felt too familiar, I had already gotten great feedback from code reviews saying that my code is a lot cleaner, as if “I had gained an extra year of experience overnight” (verbatim!).
This book breaks things down so well because the concepts are actually quite abstract, but Hunt makes the abstractions so concretely understandable (another shout-out to Made to Stick) that any programmer can benefit from this book regardless of experience.
This post may be helpful with everything being so concise, but regardless the book definitely deserves its own read. These principles will truly sink in once you dive into Hunt’s explanations, and those principles will last throughout your software engineering career.