tech: software development principles

After working on a project for a while with Agile and analyzing why some things went wrong, I came up with the following guidelines for our team. I printed them out and posted it on my cube wall so I would see it multiple times daily. I see these as supplements to or applications of the Agile principles. I don’t think these are unique to our project, so I’ll share them with anyone who can get value from them.

Here is my list:

Context is everything. If you understand the context, you will naturally choose the right solution. If you don’t understand the context, you’ll be making a decision blindly or with improper assumptions. In these very complex environments, making good decisions is critical. Understand the context, which likely means you need to talk to more people than usual.

The goals should be clear. If they aren’t, ask. I want my team to understand what the high-level goals are, and how the work they do on a daily or hourly basis helps the team to achieve those high-level goals, and why that work is important to the team. If you can’t answer that positively, then either you don’t understand the goals, you don’t understand the task, or you’re working on the wrong thing. It’s totally cool to raise your hand and say “Help me understand the goals.” It shows that you care and that you are a team player. The only stupid question is the one that is not asked, especially when the answer matters to you.

Prioritize. And revisit the priorities. Start with the understanding that you can’t complete everything that is desired. Work on the most important things first. Front-load the risky items so you have time to deal with unexpected bad outcomes. Priorities will help you decide what to leave undone, as you will have to leave some things undone. Revisit the priorities periodically to check if they need to be adjusted, especially due to new information.

Work on the right thing at the right time. This is a big one. If we start with the assumption that there will always be more work to do than time allows, we can’t do it all. And human nature makes it too easy for urgent things to displace the truly important things. So pick the right thing that is most important at that moment. And the right thing will vary based on a lot of factors. It varies over time, usually a lot. Every day when I arrive at work, I ask myself the question “What do I need to be working on today?” and re-evaluate.

On average, go at a sustainable pace. There will be peaks and valleys in workload, but getting burned out is bad for everyone: the employee, the employer, and the customer. An unsustainable pace for too long will increase technical mistakes and increase technical debt. Push back if an unsustainable pace is creating a bubble that will collapse.

Play the long game. Battles will come and go, but this is about winning the war. Don’t mistake a battle for the war. This means we may need to take some hits in the short term in order to succeed in the long term. A few customers simply aren’t going to work out, it’s OK to let them go elsewhere.

Understand the “iron triangle”. For a product that is considered mature and customers have high reliance on working properly (i.e., bank transfer), quality needs to be there. In the iron triangle, the 3 vertexes are resources (including time and people), content, and quality. If  you move one vertex, the other two will also need to change. If you choose to increase the quality, it may take more resources and/or less content. In my current position, we generally have flexibility for time, but generally not flexibility for quality. If you have a product where time-to-market is important and shortened, then quality and/or content may need to be lessened.

Have a constant sense of urgency. But don’t go so fast that you make mistakes or take shortcuts. Most of my coding mistakes are when I am trying to hurry. And fixing those mistakes slows down me and others. Remember the classic horror movie scene where the guy with the chainsaw is very slowly lumbering toward the victim, but the victim is either screaming and holding still, or trying to run away so frantically that they are tripping over themselves and still going to get caught by the slowpoke lumbering murderer? Honestly, if they would just calm down and walk away at a decent pace everything would be fine.  Sometimes you need to slow down to go faster.

Too much communication is better than too little. As developers, we may not be the best communicators. But communication is essential to a team functioning properly, so we can all do the right thing at the right time. The team’s goals are bigger than what any one person can do. When in doubt if you should let someone know about something, always let them know. Cross-training and occasional role shadowing can be really helpful.

Think hard. And ask for advice. Challenge yourself to come up with a better plan, even after you’ve come up with a good plan. Benefit from the knowledge and diversity of those around you. There is value in personal interactions. Don’t be a heads-down soloist.

Exercise due diligence, but don’t spin your wheels. Before you go to someone to ask them a question, do some homework. But don’t spend so much time doing homework research that you don’t get the job done in a reasonable time. If you get stuck, back up and take a look at it, and if you still can’t figure it out, then go ask for help. Asking for help is totally OK. You would gladly provide help to someone if asked, right? This is a team, not an set of isolated individuals. Instead of brute forcing something, try to work smarter and efficiently.

Always keep it simple. Complexity is our #1 enemy. We must be diligently fighting this enemy wherever it silently creeps in. It is much easier to achieve the *-abilities (maintainability, usability, etc.) when the thing is simple. Its corollary: pre-optimization is the root of evil. For example, realize that if you want to cache that data, you now need to manage the cache and deal with synchronization. Keep it simple, and then optimize only what the data tells you is actually needed – it’s likely a lot less than you expect. Just because you can create something doesn’t mean you should.

Build for elegance and craftsmanship and simplicity. Then it becomes stable, fast, intuitive, etc., and most importantly, predictable. In our organization, the goal is to not finish quickly, the goal is to finish at a good place. If you need more than one attempt to finish at a good place, do it. Obviously, if you can finish at a good place quickly, that would be nice, but quick is not the primary goal.

Do the right thing, even when it takes more time than the easy thing. In the best case, not doing the right thing creates debt. It is better to do a few things well than a lot of things poorly.

Obey the model. Perfectly. Models exist to provide rules of operation. Where rules exist, assumptions get made. When the model is deviated from, even in small amounts, assumptions get broken, unexpected behavior occurs, and complexity ensues. And stuff breaks.

Make the best customer experience. Don’t trade “what is best for the customer” with “what is easy for us”. Pleasing the customer over the long term is your revenue source.

Break up the large stories into digestible-size pieces. If the size of the story is too big, you won’t get it done, and it will just keep rolling over iterations. The way to eat an elephant is one bite at a time, properly swallowing between each bite, pacing yourself.

Any task which is more than trivially repeated, especially if accuracy is important, and can be reasonably automated, must be automated. The only way to scale without a huge increase in human resource cost and human errors is to automate. This also frees you up from the mundane to focus on high-value items. In the long run, the investment will not only be worth it, it will be necessary.

When it can be done reasonably, write tests to go with your code. I’m not suggesting about going crazy with unit tests, in my experience that’s not what usually breaks. With solid developers, it’s the integration function that cuts across components is the more frequent issue. But those are also generally the hardest to automate testing for. But if you can automate integration testing, then you can run it frequently and inexpensively, which will yield great payoffs in bug finding and QA cost savings. Scale!

Don’t let “perfect” get in the way of “done”. It is human nature to want to get something perfect before sharing it. (I live with visual artists.) Left unchecked, I think it also is human nature to want to endlessly tweak things, which ultimately probably don’t make meaningful differences. So sometimes we need to call it and get it out the door. Development that we do that doesn’t get used doesn’t realize its value. There absolutely will be times where your code doesn’t need to be perfect. For example, perhaps the primary business goal is speed-to-market, and the product is expected to remain pliable so customer feedback can guide its future direction, and it is understood to not be mission-critical for the customer. So don’t invest time in becoming perfect because the product is expected to change, and you’ll end up changing that code anyway. “Doing the right thing at the right time” can mean that you are building things to be good enough, not perfect. (Rules, including the ones here, should always be interpreted with an “as reasonable” clause.)

Test your code before you merge it. Have proven confidence that it works instead of expecting QA to catch basic problems. We currently have a dedicated QA team. But don’t get lazy and expect them to catch issues in your code. The QA team finding a problem in your code should be an exception and not the rule. The QA team is generally better at finding edge cases and interesting integration scenarios. But you as the developer should do enough testing of your own code in your development environment before you git commit + push that you have confidence in your code because you saw it actually do the right thing, not because you are arrogant.

Be totally honest. If there is something not right that is lurking below the surface, bring it to the surface so it can be seen and resolved. Otherwise it may come back and bite us at a bad time as a costly surprise. If something isn’t right, do or say something. And not just “Hey, someone needs to do something about this.” Instead, ask yourself “what am I (not someone else) going to do about it?”

Sense and respond. Once you are on a path, consistently look around and see what is going on. And compare that to your decision to originally get on this path, because you may find that at this new point that another path makes more sense because now you have new information. If a change makes sense, then change. As the saying goes, “the first step in getting out of a hole is to put down the shovel”.

Everything that does something for you also does something to you. (I came up with this quote.) That cool third-party library that helps you get something done easier, will also place limitations on you. Or that business partnership that that you outsource to can become a dependency over which you don’t have control. Be careful.

When something is intended for production, get it to be correct before the launch. It gets way more difficult afterwards. Once a customer starts to rely on something, even though it was a temporary hack, it becomes very difficult to pull it back. It will be a better experience for everyone to do it correctly before production. An uncooperative cat is hard to get back into the bag, and injuries are usually involved.

Don’t accumulate technical debt. Time-slice in debt reduction. If you aren’t making explicit debt payments in each release, you’re probably accumulating debt. Just like in the financial world, debt is a burden.

We’re not paid to know everything, we’re paid to figure things out. It’s not reasonable to expect a developer to know everything. It would certainly be cool if we did, but in today’s complex world that is pragmatically impossible. However, our value as developers is to efficiently figure things out and then translate that understanding into an implemented deliverable.

It is not an admission of failure to ask someone a question, and you will not be looked down upon. I want folks to be committed to the team (not just to themselves), and have empathy for others that is a source of helping. If someone comes to you with a reasonable question, would you be offended or look down on them? Of course not. So they shouldn’t do the same to you. You can also think about it this way: You are a member of a team. The goal is not only for the individual to succeed, but also for the team to succeed. If you have more to do than one person can handle and get overwhelmed, ask for help. Asking for help is not a sign of your weakness or failure, it is a sign of you caring for the success of the team.

That’s my list for now. There probably will be updates as new lessons are learned and articulated.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: