A Career Advice Newsletter For Software Engineers

Seven Books For Advancing Your Soft Skills As A Programmer

Throughout your programming career you will do a lot of reading. You’ll read code that you’ve written, code that others have written, and documentation.

When you’re not coding you’ll be reading lots of blogs and tutorials, especially early in your career when you’re learning the fundamentals of programming and how to construct programs that do what you want. Most of this information is freely available online, others you may have to pay for. While these can be great resources when learning, in my experience no blog or tutorial will come close to the quality of information you will find in a good book. Paying for a good book is well worth the cost. The amount of knowledge you can learn from some books will pay off tenfold throughout the course of your career. Here is a collection of books I recommend that focus on career advice and best practices for the soft skills side of your programming career. Regardless of which language or technology stack you work in, these books offer tips, techniques, and best practices that anyone can apply to their work.

The Pragmatic Programmer: From Journeyman to Master

I finally got around to buying and reading this book after having it on my list for years, and I regret not reading it sooner. The book covers all areas of software development from how to define requirements before the project starts to writing testable, modular code. I had always heard it’s a book that every software engineer should read and I think it presents advice that is applicable to anyone that writes code professionally from beginners to expert programmers.

Soft Skills: The software developer's life manual

This is another book that I believe every programmer should read in their career. The earlier on in your career the better. The focus of this newsletter has always been around the soft skills side of software development and and this book offers advice covering various aspects of your career. The book will help you determine which career path you want to take when it comes to specializing your skills in a specific technology or generalizing across a number of useful technologies.

It presents advice on interviewing, productivity, marketing yourself, physical and mental health, and even financial advice specific to software engineers. There’s more to your career than just writing code, and this book will help you navigate your career while you’re away from the keyboard.

The Mythical Man-Month: Essays on Software Engineering

This book puts a focus on project management for software engineering projects. It provides a number of short essays that are easy to digest and understand, with some software engineering facts thrown in. You’ll learn about the importance of communication and the nature of human beings when it comes to project management. This is another excellent book that offers timeless advice that any software engineer will find useful.

Coders at Work: Reflections on the Craft of Programming

Coders at Work provides an in depth look at some of the most influential programmers of our time. You’ll read through 15 interviews with accomplished programmers like Peter Norvig, Donald Knuth (famous CS Professor), and Brendan Eich (creator of Javascript). You’ll learn about a typical day is like for these programmers, along with how they got interested in programming, what interesting problems they enjoy solving, and what they think the future holds. It’s a fascinating look into the minds of some of the giants whose shoulders we stand on.

And here some technical books that I’ve found useful in my career. These are good to have on hand and can be used as references if I need to lookup specific topics.

Design Patterns: Elements of Reusable Object-Oriented Software

This is book that every programmer should have on their bookshelf. The four authors (Gang of Four) literally wrote the book on design patters. Any object oriented software system has roots that reach back to patters outlined in this book. You’ll learn about commonly used patterns such as the singleton and factory patters, and learn about lesser known patters such as the strategy and memento patterns. The examples are in either Smalltalk or C++, but don’t let that scare you. They are presented in a way that is easy to understand and apply to whatever programming language you prefer.

Code Complete: A Practical Handbook of Software Construction

While this is a very long book, it is packed with tons of guidelines and best practices for crafting quality software. McConnel covers topics such as constructing classes, using data structures and control flow structures, debugging code, and refactoring. The book is well-written and in a format where you can skip around and use it as a reference. There’s no need to read it front to back. The book offers lots of techniques for managing complexity which is a valuable skill that every software engineer should spend time learning.

Clean Code: A Handbook of Agile Software Craftsmanship

Clean Code provides readers with tools and techniques for “cleaning code” as you make changes to your codebase. The book provides lots of code examples and the author challenges you to think about what is right and wrong about each code sample. Readers will gain an understand about how to tell the difference between good code and bad code, how to create good names, functions, and classes, and how to format code for maximum readability.

A Programmer’s Guide To Risk Management

In part 1 of this post I explained that a software engineer’s job is to create value while minimizing risk. We looked at a number of ways software engineers can create value both with code and without. In part two we’ll dive deeper into how you can minimize risk for your team and your company. The software we write is complex with lots of moving parts. There’s a number of things that could go wrong at any moment and it’s our jobs as software engineers to identify what can go wrong and put safeguards in place to reduce the potential for things to go wrong.

Minimizing risk by fixing bugs

We’ll start with a simple example first. Bugs exist in every software program. Software bugs present risk to the business in a number of ways. Trivial bugs may seem harmless, such as a button that doesn’t do anything when clicked, or an error message that doesn’t make sense when submitting a form, but they contribute to poor user experiences which could lead to lost revenue.

Severe bugs pose a significant risk to the company, such as the billing system charging the wrong amount, an exception getting thrown that prevents the customer from placing an order. These kinds of bugs are considered higher priority due to the fact that they have the ability to affect revenue.

Every programmer should learn how to triage bugs by severity and priority and understand when a bug needs to be fixed right away or when it can wait.

Minimizing risk by having a plan

“Every battle is won before it’s ever fought”

- Sun Tzu

Have a plan for everything. Planning ahead forces you to think about the steps you need to take in order to accomplish the task at hand. It forces you to think about what can go wrong and allows you to prepare yourself in the event those scenarios present themselves.

Have a deployment plan for every change you deploy to production. Write out the steps you need to do on a pad of paper next to your keyboard so you can refer to it during the deploy. Or create a checklist in your favorite notes app. Don’t be afraid to run the procedures by a coworker before executing the deploy. In fact, you should run your plan by a second pair of eyes just for good measure. It’s possible you’re forgetting a critical step. Every deployment should also have a rollback plan. What is the plan in case something goes wrong during deployment? Will you rollback to a previously known good release? Will you disable that feature flag until you can confirm the bug is fixed? If you ran a database migration during the deploy, will it be safe to rollback the migration?

Knowing what you should do ahead of time if something goes wrong will help you reduce the risk making changes to production.

Don’t attempt to make complex changes all at once. It’s better to break down the problem into smaller changes that carry less risk. If you need to rename a database column, it’s better to add a new column and change the code to use the new column first. Then you can deploy a second migration to backfill the new column with values from the old column. Once you’re positive you’ve removed all instances in the codebase that reference the old column, you can remove it in a third migration. If possible, you should take a snapshot of the table before removing the column so you have that data backed up in case you need it in the future. Breaking down complex changes like this into multiple deployment steps helps to prevent the possibility of things breaking in production. Each change you make will be unique and you won’t always be able to break it down into smaller steps, but you should always try to if you can.

Minimizing risk by validating input

Input validation is another area that is often overlooked, but can cause issues later on. You should get in the habit of validating not just user input, but input at all levels of your codebase. Every time you write a new function, think about what parameters you’re passing in, and add the appropriate checks at the beginning of the function. Input should be the first thing you do in an API endpoint, a controller action, and all methods in your codebase that accept parameters. If your input does not meet certain operating criteria needed for that function to perform what it needs to do, return an appropriate error value so that it can be handled properly. Input can also be config files that you deploy to production. Often times a bad configuration can take down a site in seconds once it’s been deployed.

Forgetting to validate input is one of those things that you won’t notice until it’s too late. Modern frameworks offer built in validation helpers that do most of the heavy lifting for you, and there’s a number of open source libraries that you can use if your framework doesn’t already. Think of it as preventative medicine. If you put in the effort now you can avoid lots of problems in the future.

Minimizing risk by controlling access

Access control is another risk that tends to be overlooked, especially at smaller companies. As businesses grow it becomes necessary to prevent certain users, both internal and external, from performing certain actions. Public companies are required by law to restrict access to production systems to only a select few people. Critical components of your system should not be accessible to everyone. Deployment systems, user management, production databases, are a few examples where you should control access.

Even though it may not be your job to control who gets access to certain components, it’s something to keep in mind. You won’t want to explain to management that the new employee wiped out the production database on their first day because you accidentally gave them the wrong database credentials during onboarding. Just because you have access to certain systems does not mean everyone should.

Minimizing risk by writing automated tests

Investing resources into building out an automated testing suite will offer long term benefits, including lowering the probability of introducing code that breaks your system. As codebase complexity grows, making large changes to the system become very difficult. Having a test suite with good code coverage allows you to refactor components and replace modules with confidence. Running your automated tests frequently will help you catch bugs before they reach production.

Minimizing risk by creating processes

Every business will have certain routine tasks that need to be executed, even on the engineering team. There will be certain tasks that need to be performed, usually in a specific order, that are necessary to keep the business running. Running payroll, new employee orientation, deploying to production, and onboarding a new customer are just a couple examples of routine tasks. Making sure these tasks are performed the same way every time ensures that they are done correctly and that everything that needs to get done does get done. Things would get very messy if every new customer implementation was handled differently. Their data could be inconsistent and their account may not get configured correctly, causing issues for you down the line. Creating and maintaining processes for all these tasks is essential to running a lasting business.

Having an off boarding process documented for when an engineer leaves your team is critical, especially if they are a disgruntled employee. You’ll want to make sure you’ve revoked access to all the necessary servers, databases, and services that are essential to keeping the business secure. You’ll be in for a rude awakening if you find out that a disgruntled employee took some malicious action after they leave.

Minimizing risk by writing post mortems

“Failure is only the opportunity to begin again more intelligently.”

- Henry Ford

Things will eventually go wrong. Something will break and you’ll have to scramble to find a fix. Use this as an opportunity to learn from your mistakes. Depending on the size of the incident, consider writing a post mortem report once things have calmed down. Post mortem’s help you to reflect on what went wrong and what steps you took to resolve the issue. A good post mortem will also list out what steps will be taken in order to prevent the incident from happening again. Adding additional processes, stricter code reviews, separation of duties, and other changes that may come from the result of a post mortem will help to minimize the risk of a second incident. Not only does it help you learn from your mistakes, but other people on your team will benefit from understanding what caused the incident, and how the issue was resolved. Sharing that knowledge reduces the chance of it happening again.

Minimizing risk by informing management

Some level of risk is allowed, but often times it might not be your decision to make. If you find a security vulnerability in your codebase, inform your manager and work with them to triage the priority in getting it fixed. Get it in writing if possible. It can be frustrating, but It may be out of your control in deciding whether or not to fix it immediately. If you can at least prove that you took the appropriate steps to disclose the vulnerability then you can limit your personal liability should something catastrophic happen.

A Programmer's Guide To Building Value

As software engineers it’s easy for us to get caught up in the details of our jobs. You do what you do because we love the challenge of solving difficult problems. There’s no better feeling than constructing code and seeing it come to life. The fact that you can write code once and run it over and over again is a special thing. You’re a creator. The thrill of creating something from nothing and watching people interact with your creation is addicting. But as an engineer you have to remember why you are here. You’re fortunate enough to earn a good salary while doing what you love because your unique skill set gives you the ability to create value. This ability often aligns with your employer’s incentive to create value for customers, hence why they pay you competitively.

As you advance in your software development career, you’ll realize that your job is to create value while minimizing risk. Adding value does not necessarily mean writing code, as I’ll explain shortly. As you gain experience you’ll also understand that all code you write adds risk, in varying degrees. Every change you make opens up the possibility for something to go wrong. And things will go wrong. It’s up to you to identify what can go wrong, when it can go wrong, and reduce the probability of those scenarios happening. Knowing this will take you very far in your career.

So how do you, as a software engineer, add value?

Write code that makes money

This should be the most obvious way in which value is created. The code you write often times directly relates to your employer’s revenue. The catalog and checkout pages you built allow customers to purchase products from your website. Customers pay a monthly subscription for access to the document conversion service that you wrote. Or perhaps other corporations secure their networks thanks to the proprietary algorithm you helped write. Knowing how the code you’re writing fits into your employer’s business model is essential for understanding how decisions are made. Not everything you work on is going to be fun and exciting, but knowing that what you’re working on will drive the business forward helps you power through those dull and frustrating weeks that every engineer goes through.

Write code that saves money

It’s not always easy to identify how the code you’re writing contributes to the business making money. Often times what you’re working on has an indirect affect on revenue by /saving/ money. Adding address validation to your checkout page most likely isn’t going to increase your conversion rate, but it will help to reduce fraudulent charges and increase the deliverability of every order that ships. That new reporting tool you’ve spent two weeks building will save the sales team three hours every week. That’s more time they can spend on sales calls. It’s also entirely possible that the code you write makes money for your business while saving money for your customers.

Understanding the purpose of every project and how it fits into your company’s long term strategy won’t necessarily help you write better code, but it will provide more context for understanding how the management team makes decisions about where to allocate resources.

Add value without writing code

Your jobs as software developer goes beyond just writing code every day. Sharing your knowledge of the codebase with your team is another way to add value indirectly. Thorough documentation is a quality that all successful companies share. It contributes to business continuity as new employees join and seasoned employees get promoted and move on. It contributes to increase efficiency when ad-hoc tasks need to be executed, and it allows business processes to scale beyond a single person.

Add value by creating processes

Process ensures that certain tasks are done in a repeatable fashion, which drives efficiency and allows businesses to scale. Whether you’re creating a process for on boarding new engineers, or tweaking your team’s software development lifecycle process, making sure each step is documented thoroughly adds value by promoting consistency. There will be times where your processes will break down, so it’s worthwhile to go back and make adjustments to prevent the same issues from happening again in the future.

Moving the business forward

There’s a number of other ways you can create value for your team and your company that I haven’t written about here. Knowing how your work relates to building value for your company gives you the ability to suggest improvements and shows the management team that you understand what needs to be done to drive the business forward. There will be times in your career where you’ll have to make your own decisions on which projects you should work on, and knowing which projects add the most value will help guide you in your decision making process.

Next I’ll talk about part two of this post - minimizing risk, and how it relates to building value as a software engineer. Enjoy the week, and take some time each day to stop what you’re doing and ask yourself “How does this add value?”

Adding Value By Contributing To A Wiki

Transferring knowledge through written sources has been around for centuries. It’s one of the reasons that has contributed to the progress of society and is one of the reasons you are able to read this post today. By writing down what we know and what we’ve learned, we are able to stand on the shoulders of those that came before us and continue to make progress because we do not have to re-discover what has already been discovered.

As a software developer, documenting what we know about the systems we work on is vital for us to keep those systems running and the business afloat. It’s good to get in the habit of documenting everything you can, and it’s one of the things I notice junior developers tend to forget the most.

An easy way to contribute

Contributing to the wiki is one of the easiest things you can do to build value for your team. By documenting what you learn, or improving upon existing documentation, you’re laying the foundation for others to build on that knowledge.


When you first start a new job as a software developer, you’ll usually go through the steps to get your development environment set up. You should always take notes during this process. If you’re following an existing list of steps to get your dev enviornment set up, then you can use your notes to improve the onboarding process for the next engineer. If for some reason there is no existing documentation and you were working with another engineer to set up your environment, then you can use your notes to create new onboarding documentation for the next engineer that joins your team. Taking notes while setting up your development is the easiest and fastest way for you to add value to your new team. Good onboarding documentation is vital to getting new hires up and running as soon as possible.

Documenting processes

If there are any routine processes that requires a user to manually run through a list of steps, they should always be documented. If you try to run through the steps from memory there is always a risk that you will forget a step. It’s tough to me mentally alert 24/7. There will be days where you didn’t sleep well the night before, or you’ll feel sleepy in the afternoon from a big lunch. It’s easy to forget things when this happens so having your processes documented is a good way to ensure you’re following the same steps every time the process needs to be run.

You should also recognize that other engineers may be the ones running these manual processes in the future. They may take over responsibility for that part of the system, or you may be out of the office on vacation, so these processes need to be documented to ensure that other engineers can follow the steps exactly in order to run the processes when you’re not there to do it yourself.

Notes for your future self

It’s impossible for us to remember everything we do every day. And it’s even harder for us to remember what we did six months or a year ago. Documenting everything in a wiki not only benefits other engineers on your team, but it allows you to leave notes for yourself about how you did some task in the past. There will be times when you can’t remember the exact command you ran a year ago to free up disk space when the disk was full. But if you document exactly how you fixed the issue a year ago you’ll be able to look up the exact commands to run next time it happens. You won’t have to search google and stack overflow again to rediscover something you already figured out how to do. It will save you time and money in the future.

Transferring knowledge

One of the biggest benefits to documenting what you know about the software you write is the ability to transfer knowledge to other members of your team, including future developers. If someone posts a message in slack asking how to do something, you can save a lot of time by sending them a link to the wiki that describes what they’re asking about.

Engineers will come and go

You’ll come across times in your career where engineers who build the systems leave the company and you will have to take over responsibility for developing and maintaining what they built. Those engineers will have a deep understanding of how (and why) the code they wrote works the way it does. In order to ensure you have the information you need to maintain what they built, you should make sure they do a knowledge dump of everything they know about that system before leaving.

Encourage others to contribute

In addition to contributing to the wiki yourself, it’s good to encourage your teammates to document what they know too. If they’re explaining the steps they took to fix a bug, ask them if they could document how they went about fixing it. When you’re reviewing code for other engineers that adds a new feature, that’s a good opportunity to ask them if they could document how that feature works.

Maintaining the wiki

Just like code, there comes a cost to maintain the wiki once people have contributed to it. As the codebase changes, certain pages on the wiki may become out of date. If you don’t update or remove these pages as their information becomes outdated then it will create confusion down the line. It’s not a good start if a new hire shows up on their first day and see two different wiki pages with instructions to set up their dev environment. You’ll really regret it if the site is down and you refer to the wiki to figure out what steps to take in that event and it’s talking about a part of the codebase that was removed 6 months ago.

You wiki should be a constantly changing set of documents and it takes time to keep it up to date, but a good wiki adds more value than can be measured. It allows you to share knowledge about the system across time and within your team. Getting in the habit of contributing and maintaining your teams wiki is a quick and easy way to add value, even if it isn’t writing code.

Learn how to write effective automated tests

If you want to excel in your career as a software developer, you should learn how to write effective automated tests.

I’ve come across too many engineers who can build amazing applications, but don’t have the slightest clue about how to write tests for their code. The purpose of this post isn’t to explain how to write a unit test, or whether you should write unit tests vs. integration tests. I’m intentionally being vague here when it comes to defining a “test” because there are a number of different testing strategies. There are plenty of books and resources on the internet that will teach you the difference between a unit test, an integration test, a smoke test, or an end-to-end test and how to write each one.

The purpose of this post is to stress the importance of having the skill to write effective tests for your code, no matter what testing strategy you employ. As a junior software developer, including valid automated tests with your pull requests will make you stand out among your peers and position yourself as an engineer whose code is stable and reliable. By including automated tests with your code, you’re effectively saying “here’s my code, and here’s proof that it works correctly.”

I also want to stress that automated testing is not the holy grail of software development. Just because you include tests with your pull requests does not make that function you added any better. Your tests are only as good as the assertions you make. It’s entirely possible to write faulty tests, and tests that don’t cover all possible corner cases, leaving the potential for bugs in your code.

Testing is just as much an art form as it is a science. The more you work on writing tests for your code, the better you will become at knowing how, and when, to write effective tests. You’ll also gain a better understanding for when it’s just not possible to write a reliable test for a piece of code. A good engineer will know when their time is better spent moving on to other bugs or features, rather than spending days figuring out how to write an automated test.

How will you know what should be tested? For starters, the code that is core to the business or project should include tests, at the very least. The code that brings in the money is code that you want to be confident works correctly. You should include tests to validate results and cover corner cases for your proprietary algorithms. E-Commerce sites should include tests to make sure the components of the checkout page are working properly. You will not want to explain to management that the business lost money because of a bug in your core product, trust me. As an engineer your job is to build value and reduce risk. Automated tests for your core product reduce risk and add reliability to the business.

How will you know when to write tests? Any bug you fix is a good candidate to ensure it won’t happen again. I don’t subscribe to the religion that is TDD (Testing Driven Development), but it is useful in certain scenarios. You’ll need to reproduce the bug before you can fix it, so set up a test to reproduce the bug before working on the fix. Write the test so that it fails, fix the bug, and confirm the test passes.

Another good candidate for automated tests is any sufficiently large refactor. If you’re refactoring the way an offline job processes incoming data, set up a test that passes with the current code. Once you refactor your code the test should still pass. I guarantee you’ll be more confident deploying your code knowing that your tests passed before and after you made your changes.

If your frontend client consumes an API that is a good candidate for integration tests. Writing a suite of tests that hit your API endpoints to verify the correct status codes is an easy way to confirm your changes on the backend aren’t affecting the client on the front end.

A suite of automated tests for your codebase is extremely valuable to any engineering team, but it’s not useful if you don’t run them often. If you have a continuous integration system, you’ll want to run your tests as often as possible when there are new changes. The earlier you are alerted that a code change broke a test, the faster you can catch bugs before they make it to production. The longer it takes to find out about a test that’s broken, the harder it will be to track down which commit was responsible.

Another point I’d like to stress is that just like production code, tests carry a maintenance weight. You will have to support any test that breaks once it’s been written, so it’s worth your time to learn how to write tests that are isolated and independent from one another. If your test requires some set up data, make sure it’s not relying on data generated or modified by a previous test. It’s also worth knowing when some code should include tests and when tests are not needed. The experience to make these decisions will come to you over time as you write tests. Each piece of code you write is unique and should be evaluated on a case by case basis as it’s written.

Learning how and when to write effective automated tests will be a valuable skill to have as you progress in your software development career. The learning curve for writing tests can be difficult when you’re just starting out. If the codebase you’re working on already has some tests in place, that would be a good place to start. Read through the existing tests to see how they set up their test data. Study the assertions they make and which corner cases they cover. You’ll also start to learn how to construct your code so that it’s modular and easily testable. This knowledge will come to you over time, but once you learn how to test effectively you’ll have a valuable skill for the rest of your career.

Here are some recommended books on automated testing:

Unit Testing Frameworks

Test Driven Development: By Example

Working Effectively with Legacy Code

If you like this post, feel free to share it on Facebook, Twitter, Hacker News, or your favorite social media platform.

Loading more posts…