Code Simplicity

Fires vs. Strategy

There’s a point that I’ve been making to engineers recently that I realized would be valuable if shared more widely.

When you do engineering work, there are different types of tasks that get given to you. Some tasks are emergencies or short-term work. We sometimes call this “putting out fires,” especially when the work relates to handling something that is urgently broken or immediately needed without delay.

Other tasks are strategic in nature. You have collected information about what is needed and/or wanted from your users, you’ve designed a solution, and you’re working toward it methodically and intelligently.

It is important to understand when you are doing which type of work, and to think about them differently. Continue reading

1 Comment

How to Learn to Program

One question that people ask me all the time is, “How do I become a programmer?” Or, “How do I learn to program?” Continue reading


How to be a Great Programmer: Awareness, Understanding, and Responsibility

There are three key factors to being or becoming a great programmer: awareness, understanding, and responsibility.

I’ve talked a lot about the subject of understanding. Heck, I even named my most recent book Understanding Software. In particular, I’ve pointed out many times that the better you understand something, the better you will do it.

However, there are two other factors that go along with understanding to make somebody into an excellent software developer. In brief, if you aren’t aware of a problem, there’s no understanding to be had. And if you don’t take action to solve the problem (which starts with taking responsibility for it) then you can’t do anything about it.

There’s a lot to know about each of these points, though, and they are key points when one looks at how to become a better software developer. So I want to go into each one in more depth with you, here. Continue reading


Understanding Software

Hey everybody. I’ve published a new book! It’s called Understanding Software.

The book contains all of the content that I’ve written on software development and working in teams since the publication of Code Simplicity, plus some entirely new content that’s never been published anywhere. In fact, it contains one of my favorite essays that I ever wrote from back in 2008 but never published before. So there’s that for you. All the content has been put into a beautiful layout, then curated and organized for maximum readability.

It’s something I’m actually really happy with, and I’m looking forward to hearing what you have to say about it, too.

From the Publisher

Understanding Software covers many areas of programming, from how to write simple code to profound insights into programming, and then how to suck less at what you do! You’ll discover the problems with software complexity, the root of its causes, and how to use simplicity to create great software. You’ll examine debugging like you’ve never done before, and how to get a handle on being happy while working in teams.

Max brings a selection of carefully crafted essays, thoughts, and advice about working and succeeding in the software industry, from his legendary blog Code Simplicity. Max has crafted forty-three essays which have the power to help you avoid complexity and embrace simplicity, so you can be a happier and more successful developer.

Max’s technical knowledge, insight, and kindness, has earned him a status as a code guru, and his ideas will inspire you and help refresh your approach to the challenges of being a developer.

What you will learn

  • See how to bring simplicity and success to your programming world
  • Clues to complexity – and how to build excellent software
  • Simplicity and software design
  • Principles for programmers
  • The secrets of rockstar programmers
  • Max’s views and interpretation of the Software industry
  • Why Programmers suck and how to suck less as a programmer
  • Software design in two sentences
  • What is a bug? Go deep into debugging

You can get it on Amazon, direct from the publisher, or in any other place where programming books are sold.



Kindness and Code

It is very easy to think of software development as being an entirely technical activity, where humans don’t really matter and everything is about the computer. However, the opposite is actually true.

Software engineering is fundamentally a human discipline.

Many of the mistakes made over the years in trying to fix software development have been made by focusing purely on the technical aspects of the system without thinking about the fact that it is human beings who write the code. When you see somebody who cares about optimization more than readability of code, when you see somebody who won’t write a comment but will spend all day tweaking their shell scripts to be fewer lines, when you have somebody who can’t communicate but worships small binaries, you’re seeing various symptoms of this problem.

In reality, software systems are written by people. They are read by people, modified by people, understood or not by people. They represent the mind of the developers that wrote them. They are the closest thing to a raw representation of thought that we have on Earth. They are not themselves human, alive, intelligent, emotional, evil, or good. It’s people that have those qualities. Software is used entirely and only to serve people. They are the product of people, and they are usually the product of a group of those people who had to work together, communicate, understand each other, and collaborate effectively. As such, there’s an important point to be made about working with a group of software engineers:

There is no value to being cruel to other people in the development community.

It doesn’t help to be rude to the people that you work with. It doesn’t help to angrily tell them that they are wrong and that they shouldn’t be doing what they are doing. It does help to make sure that the laws of software design are applied, and that people follow a good path in terms of making systems that can be easily read, understood, and maintained. It doesn’t require that you be cruel to do this, though. Sometimes you do have to tell people that they haven’t done the right thing. But you can just be matter of fact about it—you don’t have to get up in their face or attack them personally for it.

For example, let’s say somebody has written a bad piece of code. You have two ways you could comment on this: Continue reading


The Fundamental Philosophy of Debugging

Sometimes people have a very hard time debugging. Mostly, these are people who believe that in order to debug a system, you have to think about it instead of looking at it.

Let me give you an example of what I mean. Let’s say you have a web server that is silently failing to serve pages to users 5% of the time. What is your reaction to this question: “Why?”

Do you immediately try to come up with some answer? Do you start guessing? If so, you are doing the wrong thing.

The right answer to that question is: “I don’t know.”

So this gives us the first step to successful debugging: Continue reading


Refactoring is About Features

There’s a point that I made in the book but which I have had to point out to people a few times since then, and so I wanted to emphasize it a bit more.

When you clean up code, you are always doing it in the service of the product. Refactoring is essentially an organizational process (not the definition of “organizational” meaning “having to do with a business” but the definition meaning “having to do with putting things in order”). That is, you’re putting in order so that you can do something.

When you start refactoring for the sake of refactoring alone, refactoring gets a bad name. People start to think that you’re wasting your time, you lose your credibility, and your manager or peers will stop you from continuing your work.

When I say “refactoring for the sake of refactoring alone,” what I mean is looking at a piece of code that has nothing to do with what you’re actually working on, saying, “I don’t like the way that this is designed,” and moving parts of the design around without affecting the functionality of the system. This is like watering the lawn when your house is on fire. If your codebase is like most of the codebases I’ve seen, “your house is on fire” is probably even an appropriate analogy. Even so, if things aren’t that bad, the point is that you’re focusing on something that doesn’t need to be focused on. You might feel like you’re doing a great job of reorganizing the code, and probably you are, but the point of watering your lawn is to have a nice lawn in front of your house. If your refactoring has nothing to do with the current product or feature goals of your system, you’re not actually accomplishing anything other than re-ordering something that nobody is using, involved with, or cares about.

So what is it that you want to do? Continue reading


Effective Engineering Productivity

Often, people who work on engineering productivity either come into conflict with the developers they are attempting to help, or spend a long time working on some project that ends up not mattering because nobody actually cares about it.

This comes about because the problem that you see that a development team has is not necessarily the problem that they know exists. For example, you could come into the team and see that they have hopelessly complex code and so they can’t write good tests or maintain the system easily. However, the developers aren’t really that aware that they have complex code or that this complexity is causing the trouble that they are having. What they are aware of is something like, “we can only release once a month and the whole team has to stay at work until 10:00 PM to get the release out on the day that we release.”

When engineering productivity workers encounter this situation, some of them just try to ignore the developers’ complaints and just go start refactoring code. This doesn’t really work, for several reasons. The first is that both management and some other developers will resist you, making it more difficult than it needs to be to get the job done. But if just simple resistance was the problem, you could overcome it. The real problem is that you will become unreal and irrelevant to the company, even if you’re doing the best job that anybody’s ever seen. Your management will try to dissuade you from doing your job, or even try to get rid of you. When you’re already tackling technical complexity, you don’t need to also be tackling a whole company that’s opposed to you.

In time, many engineering productivity workers develop an adversarial attitude toward the developers that they are working with. They feel that if the engineers would “just use the tool that I wrote” then surely all would be well. But the developers aren’t using the tool that you wrote, so why does your tool even matter? The problem here is that when you start off ignoring developer complaints (or don’t even find out what problems developers think they have) that’s already inherently adversarial. That is, it’s not that everything started off great and then somehow became this big conflict. It actually started off with a conflict by you thinking that there was one problem and the developers thinking there was a different problem.

And it’s not just that the company will be resistive—this situation is also highly demoralizing to the individual engineering productivity worker. In general, people like to get things done. They like for their work to have some result, to have some effect. If you do a bunch of refactoring but nobody maintains the code’s simplicity, or you write some tool/framework that nobody uses, then ultimately you’re not really doing anything, and that’s disheartening.

So what should you do? Continue reading


Measuring Developer Productivity

Almost as long as I have been working to make the lives of software engineers better, people have been asking me how to measure developer productivity. How do we tell where there are productivity problems? How do we know if a team is doing worse or better over time? How does a manager explain to senior managers how productive the developers are? And so on and so on.

In general, I tended to focus on focus on code simplicity first, and put a lower priority on measuring every single thing that developers do. Almost all software problems can be traced back to some failure to apply software engineering principles and practices. So even without measurements, if you simply get good software engineering practices applied across a company, most productivity problems and development issues disappear.

Now, that said, there is tremendous value in measuring things. It helps you pinpoint areas of difficulty, allows you to reward those whose productivity improves, justifies spending more time on developer productivity work where that is necessary, and has many other advantages.

But programming is not like other professions. You can’t measure it like you would measure some manufacturing process, where you could just count the number of correctly-made items rolling off the assembly line.

So how would you measure the production of a programmer? Continue reading


Two is Too Many

There is a key rule that I personally operate by when I’m doing incremental development and design, which I call “two is too many.” It’s how I implement the “be only as generic as you need to be” rule from the Three Flaws of Software Design.

Essentially, I know exactly how generic my code needs to be by noticing that I’m tempted to cut and paste some code, and then instead of cutting and pasting it, designing a generic solution that meets just those two specific needs. I do this as soon as I’m tempted to have two implementations of something.

For example, let’s say I was designing an audio decoder, and at first I only supported WAV files. Then I wanted to add an MP3 parser to the code. There would definitely be common parts to the WAV and MP3 parsing code, and instead of copying and pasting any of it, I would immediately make a superclass or utility library that did only what I needed for those two implementations.

The key aspect of this is that I did it right away—I didn’t allow there to be two competing implementations; I immediately made one generic solution. The next important aspect of this is that I didn’t make it too generic—the solution only supports WAV and MP3 and doesn’t expect other formats in any way.

Another part of this rule is that a developer should ideally never have to modify one part of the code in a similar or identical way to how they just modified a different part of it. They should not have to “remember” to update Class A when they update Class B. They should not have to know that if Constant X changes, you have to update File Y. In other words, it’s not just two implementations that are bad, but also two locations. It isn’t always possible to implement systems this way, but it’s something to strive for.

If you find yourself in a situation where you have to have two locations for something, make sure that the system fails loudly and visibly when they are not “in sync.” Compilation should fail, a test that always gets run should fail, etc. It should be impossible to let them get out of sync.

And of course, the simplest part of this rule is the classic “Don’t Repeat Yourself” principle—don’t have two constants that represent the same exact thing, don’t have two functions that do the same exact thing, etc.

There are likely other ways that this rule applies. The general idea is that when you want to have two implementations of a single concept, you should somehow make that into a single implementation instead.

When refactoring, this rule helps find things that could be improved and gives some guidance on how to go about it. When you see duplicate logic in the system, you should attempt to combine those two locations into one. Then if there is another location, combine that one into the new generic system, and proceed in that manner. That is, if there are many different implementations that need to be combined into one, you can do incremental refactoring by combining two implementations at a time, as long as combining them does actually make the system simpler (easier to understand and maintain). Sometimes you have to figure out the best order in which to combine them to make this most efficient, but if you can’t figure that out, don’t worry about it—just combine two at a time and usually you’ll wind up with a single good solution to all the problems.

It’s also important not to combine things when they shouldn’t be combined. There are times when combining two implementations into one would cause more complexity for the system as a whole or violate the Single Responsibility Principle. For example, if your system’s representation of a Car and a Person have some slightly similar code, don’t solve this “problem” by combining them into a single CarPerson class. That’s not likely to decrease complexity, because a CarPerson is actually two different things and should be represented by two separate classes.

This isn’t a hard and fast law of the universe—it’s a more of a strong guideline that I use for making judgments about design as I develop incrementally. However, it’s quite useful in refactoring a legacy system, developing a new system, and just generally improving code simplicity.