25 Things I Learned on Software Development

Published on September 20, 2014 · 15 mins

I wanted to put together some tips about software development, customer relationships and learning in general, so I wrote this in the hope that it might prove useful to someone else. Very little of what’s written here is new stuff, but I liked the idea of having it all in one place, along with the reasoning that led me to accept it and make it a part of my methodology.

This is quite a long article, so let’s just get to it.

Mistakes

#1: It’s always your mistake

Never assume that that strange behaviour you’re noticing is being caused by some API or third-party dependency. It’s always your mistake until you reliably prove it isn’t. Start blaming others (although unintentionally) for screwing up and your software will suffer along with your users.

#2: Don’t be afraid

Do you know the difference between the master and the fool? The master has made ten thousand mistakes. The fool has made ten mistakes a thousand times each. Don’t be afraid of experimenting, of screwing up: it happens to everyone once in a while. Of course you should try, whenever possible, to avoid making a mess, but it’s not the end of the world if you do.

#3: Be honest

So, you’ve already screwed up. What do you do? Do you pretend it never happened? Do you remove all the incriminating logs? Or do you act like a decent human being and admit it? The choice is obvious. I know it’s scary to think of the consequences, but you should always be honest with your customers. Trust me, they’ll value that.

#4: Think before fixing

Your customer is yelling at you over the phone. Nothing’s working anymore. Finally, you think you found that nasty little line of code that killed everything. There’s no time to follow standard deployment procedure, so you open an SSH session and make the change on the production machine. Except you screwed up once again, and now your production database is completely gone.

None of this would’ve happened if you had bothered testing your code before deploying it. Ten minutes less of downtime will not make a difference for your users, so take some time to plan your steps before executing them. It might save your job.


Backups


#5: Always make a backup

There are no exceptions. No, you can’t skip the backup just because you’re going to execute a SELECT, or because you’re sure your code is bug-free. All code is bug-free… until someone finds a bug. It doesn’t take much time to make a backup. How long will it take for you to find another career, though?


Version control


#6: Put everything under version control

And when I say “everything”, I mean everything. No exceptions. Everything can be put under version control. I don’t think I need to explain to you the benefits of using a version control system: you can revert any changes you’ve made; you can find out where and when a bug was introduced (and who introduced it); it makes collaboration extremely easier. There’s really no excuse not to use it.

Also, it’s important that you introduce version control early in the project. Ideally, your first commit should be of the software’s readme.

#7: Create feature branches

Never work on the master branch. Always create a feature branch, even for the smallest change. In many teams, this is mandatory: you create a branch off of master, commit your changes and submit a pull request (or whatever it’s called internally). At this point someone else reviews what you’ve written, QA ensures everything works fine and the reviewer merges it into master. Usually, everyone has the permission to merge into master, but it’s strictly prohibited to merge your own code (for obvious reasons).

#8: Keep your commits organized

This is much easier for hipsters who started developing yesterday and have never done it without version control (although that doesn’t mean they’re doing it right).

Here’s the situation: you’re versioning your software, you use feature branches and you follow all the best practices regarding pull requests and QA. However, while you’re working on something, you find a minor unrelated issue in the code. It’s a really dumb problem, something along the lines of an oddly named variable. Even though you know you shouldn’t, you fix it and it gets merged into master. More often than not, it’s not even mentioned in the commits, because you made other (major) changes to the same file and completely forgot about it.

It doesn’t seem harmful, but this practice (which I indulge in more often than I’d like to admit, especially when working solo) creates confusion in the team and makes it harder for people to trace the who, when, what, how and why of a specific change. By the time an issue arises with what you’ve done, you won’t remember having done it and your version control system won’t be able to help you.

This takes us to the next point.

#9: No “ands” in commits

Every time you write “and” in your commits, you’ve probably screwed up somehow. An example? “Fix issue #132 and implement private messaging system”. This is extreme, of course, but you get the idea. What does the messaging system have to do with issue #132? Why are they in the same commit? Why are they even in the same branch? What the hell are you doing with your life?

#10: No generic commit messages

This usually happens when you’re frustrated. Perhaps you’re trying to fix an issue for the fifth time. A commit message? Screw it! Just put “Fix issue” in there. The same goes for “Ugly hack [where?, how?, why?]”, “Refactor [what?]” and so on and so forth. Be descriptive in your commit messages. Ideally, people reading them should understand the whole reasoning behind your changes. No past simple (“Implement X” instead of “Implemented X”), a short title and a longer description of the changes made.


Testing


#11: Tests lie

When you first entered the magic world of TDD, you thought you were safe. Everything worked perfectly as long as your tests passed. You began developing features without even using them yourself. You don’t know what the interface you’re developing looks like. Why should you? Your tests will take care of everything.

Then something breaks. All’s green and nothing’s working. And it’s your users who found out about it, because you were too busy admiring that stylish 100% code coverage report.

The truth is: Tests lie. All the time. They are not perfect, even though they help a lot. You should still write tests, of course, but that doesn’t excuse you from using the app you’re developing. If it’s so boring you don’t even want to look at it, let alone use it, you probably shouldn’t be working on it in the first place.

#12: Don’t overtest

When you spend more time writing and maintaining tests than developing features, something’s wrong. Tests should take at most 30% of your time (your mileage may vary, of course). After all, your users don’t really care for testing — as long as it works, you could as well be developing on a Tamagotchi (and using Waterfall!).

Oh, and don’t even try to aim for 100% coverage. That’s just ridiculous. All these people priding themselves with testing every single line of code… stop it already! By the way, code coverage reports lie as well, and they’re only useful up to a certain extent. You should be confident you’re testing what matters and not a line more.

The question is: what does matter? Unfortunately, I can’t give a universal answer. But I can tell you what doesn’t matter: getters and setters. Of that I’m sure. The rest is for you to decide—eventually, your gut will tell you what’s worth testing and what isn’t.

#13: Beware of mocking

Remember: Mocks and stubs are treacherous paths (isn’t that cute?). This doesn’t mean you should avoid them completely: mocking is the only way to write unit tests. Just be careful your mocks are actually mocking behavior instead of inventing it, or once again you’ll end up with passing tests and broken software.

Some tools simplify this by ensuring that you’re mocking existing classes and methods, but when it comes down to it, good sense is the only thing that will keep your mocks from growing minds of their own.

#14: It’s okay to write first and test after

Yes, yes, I know: red, green, refactor. Perhaps it’s just me, but I found that this approach works well for small units (i.e. single classes) but falls short when you have to test whole features of the app (functional/integration testing), because often you’re not sure of the architecture and you need to experiment. In that case, it’s not always easy to write the tests beforehand.


Planning


#15: Take your time

Most people will tell you to ship an MVP (Minimum Viable Product) as soon as possible in the development process. I’m not against this practice per se, but people tend to have discording opinions about what an MVP is. This is usually decided with the customer, who might give you a set of features the product must have before its launch.

However, MVP is not a synonym for “buggy software”. If you don’t feel it’s ready, say it. Explain your reasons, tell the customer why it’s better to spend some more time polishing the product before a public release.

#16: But not too much

This doesn’t mean you’ve got to be a perfectionist. I’m still guilty of this from time to time: I will keep postponing an app’s launch because I don’t think it’s ready. There’s always a cool feature I want to add or a rare, hidden issue I’d really like to fix, “just in case”.

This is good: it means you’re passionate about your work and you want to produce high-quality software. But, as banal as it may sound, time is money, and it’s often better to have a partial product out the door than a complete one sitting in a private Git repo.

You’ve also got to realize, an application is never ready. There is no final state for a software: you’ll keep adding and removing features, fixing and introducing bugs, refactoring and polishing, updating and replacing dependencies till the end of time (or of your contract). People working on software, whether they are developers, designers, QA, product managers or owners, are never done. They’re costantly questioning, surveying, researching, discussing.

If this is not how you want to work, perhaps you should choose another path. Be a cook: once a meal it’s done, it’s done. There’s no fixing it. You better get it right the first time.


Dealing with customers


#17: Communicate often

The customer should be with you at every stage of the development process. If you’re working from home, that might be creepy, so a daily email with all the work you’ve done should suffice. This will save you many future headaches since any issues will be highlighted as early as possible.

You should also give your customer access to the app’s Git repository and to your project management system. (Don’t have one? Trello is wonderful!) If they don’t know how to use those tools it’s a good time for them to learn. Those skills will certainly come in handy for future projects (hopefully managed by you).

Some customers will not show interest in the project, replying late and generically to your questions, not making any decisions. Fire them. If they’re not invested in their own idea, there’s no reason why you should be, either. (No, money is not a good reason. Working with these people would be painful, so have some self-respect.)

#18: Defend your decisions

(Designers probably already know about this one.)

The customer’s the one paying you, but you’re the expert, so don’t be shy and let your voice be heard when it comes to making decisions about the software. If they don’t listen, make it clear that you won’t be able to guarantee the quality of your work. Smart people should get out of your way at this point.

When you’re on a plane, you don’t tell the pilot how to fly it because you don’t know anything about planes. The same goes for software development. Just because your customers use a computer doesn’t mean they’re seasoned engineers.

#19: Know what you (don’t) know

It can be hard to acknowledge what you’re worth, but it’s extremely important to be confident about your skills. How can you expect clients to trust you if you don’t trust yourself in the first place? If you think you’re very good at something, prove it and scream it. They’ll hear you.

This also works the opposite way: you should definitely be aware of all the fields where you lack knowledge. Since software development has become more widespread and accessible we’ve witnessed the birth of countless jacks-of-all-trades. These people proclaim themselves experts in every possible technology when, actually, they know a little of each one. They’re mediocre and they’ll never stand out, which means they’ll never be hired by important customers.

#20: Don’t undercharge

I know it’s tempting to undercharge. Something is better than nothing, right? Well, it isn’t. Every time you undercharge, you make life a little bit more difficult for the rest of us, because our customers are going to except lower rates.

You should charge your customer for the value you provide rather than the work you do. Developing a small web app might not seem much to you, so it might feel weird to charge thousands of dollars, but think about all the money that customer is going to earn/save using your software. Isn’t it worth it?

Of course, you shouldn’t defraud your customers either. It’s not cool at all, once again it damages all of us and they are probably going to find out sooner or later.

Charging is a tricky task and you’ll have to costantly adjust your rates, but eventually you’ll develop an instinct for it.

#21: Pick your projects

There’s an awful lot of developers out there that will accept almost every possible project, even the most boring ones. They don’t really care about what they’re working on as long as they’re paid. This results in poor results (I swear I didn’t do that on purpose) and lowers the standard for all other developers.

The fact is, designers are not the only creatives out there. Our job requires inspiration, too. Of course it’s possible to work on a boring project, but it’s not the same thing, is it? You have probably experienced this feeling where every line of code is painful to write and read, no matter how beautiful. You can’t really see the purpose of it. More often than not, this happens because your customer can’t see it either.

I suggest that, before accepting a project, you ask the customer about their vision. If they can’t articulate it, or if it seems boring or poorly thought, fire the customer. There’s no shame in refusing a project. Actually, it’ll allow you to develop a taste for beauty. On the other hand, if the vision excites you, get right on board. You’re probably in for some fun and satisfaction.


Learning


#22: Never stop learning

Question best practices, your choices and the customer’s. Strive to create quality work that will raise the bar in your field. If you’re used to accomplishing a certain task in a certain way, try something completely different this time. See how it works out. Embrace bleeding edge tools and new methodologies. Read articles, go to conferences, dicuss stuff.

I know how scary change is. When something works, why shouldn’t we keep doing it that way? However, if it weren’t for people willing to go out of their way and try new things, we wouldn’t probably have computers (or electricity) in the first place.

Don’t just be an executor — be a thinker. Don’t just consume, create. Your customers and your own mind will be extremely grateful for that.

#23: Don’t throw your experience away

Still, this doesn’t mean you should jump aboard whenever you see a train. If a certain tool/technique doesn’t seem to be working for you, don’t worry. Perhaps it’s more suitable for another project, or another developer. Perhaps it’s just useless or needs to mature for some time before it can be used in a production environment. Just put it aside. You can try again when you feel like it.

Don’t be that guy, the one who uses all the latest, shiniest languages, databases and development methodologies just because they’re cool. Whenever you adopt something new, understand why you’re doing it. Is it better? Is it easier? Is it faster? Does it solve a problem? If the answers to all these questions are “no,” then you’re likely being that guy.

#24: Master few, understand (almost) all

Remember earlier, when I criticized jacks-of-all-trades? Their main problem is that they don’t master any field—they have a little knowledge about all of them, but definitely not enough to do serious work.

However, knowing about different stuff (front end development, UX design, UI design, back end development just to name a few) will help you put things in perspective. It is especially useful when you’re working in a team, so that you don’t think the others are sitting there, doing nothing important or nothing at all. In other words, studying other fields is a way to build respect for the people working in them.

Master a few fields, know their ins and outs, concentrate on them for most of your time, but don’t neglect everything else. It’s stupid and it will destroy many opportunities to do cool stuff.

#25: Write about your field

There’s something I do every time I’m not sure about a certain approach or an opinion of mine. I write about it. It all seems so simple and straightforward in my head, but when I put it on paper, I sometimes find out that it doesn’t make any sense at all.

The best way to understand something is to write about it. It will help you communicate better with your customers and it will allow you to get noticed by potential ones.

See you soon?
© 2025 Alessandro Desantis