How about: Learn tools to make you more efficient? As technologies and stacks become more complicated -- and our brains retain the same capacity -- it's important to find ways to minimize the barriers to learning. It can be as simple as reducing small discomforts, which build up quickly in day-to-day-coding. I've found taking a few more minutes to pick a better color theme for my code editor can make syntax much easier to read through, especially when working with a new language.
I worry about concerns over efficiency. As much as I'd like for that to be a fairly objective word, I am forced to acknowledge it is anything but. Almost as bad as that wonderful word "quality."
For me, the challenge is to look at older technologies as often as newer ones. I don't quite believe that everything new was old at some point, but it is rather impressive to see the tricks and techniques that were used by folks years ago. To the point where I'm sometimes curious exactly what we have that is truly new. (I'm starting to learn just how powerful emacs can be, for example.)
I'm not so sure I would be so negative on the code in isolation. Yes, I think I would personally like to get my code out there more with/for others. However, I see no real reason to think this should be a moral imperative that is given to others. To the point that, while I would love for it to happen, I more want to make sure I just write more programs. Even if they are of the "I ran them exactly once just to see if it worked" variety.
I've had to maintain some codebases where the previous developer was working alone for 6-30 months. They often share the same issue that bad practices get repeated over and over and over again, not knowing something turning into a habit because no one pointed out. Also, knowing that someone will go through gives some extra motivation and makes you think twice before doing something lazy or stupid.
Also, another person can recommend you a tool, library, etc which you wouldn't have thought of. For instance, at my early days as a developer I didn't know that things like SQL schema version control exists. I got introduced to liquibase which saved me from many many hours of work making sure all environments are using the same schema.
It all depends on who sees your code and the way they approach with comments, and how do you deal with criticism.
One of the biggest ways I became a better programmer in 2013 was essentially the opposite of this advice from item 3: "Don’t be afraid of your code. Who cares if something gets temporarily broken while you move things around? A paralyzing fear of change is what got your project into this state to begin with."
I was formerly working in an environment where no one was really responsible for making sure that features worked consistently or that new features didn't incidentally break old features, and the result was a huge amount of frustration and wasted time. Learning to spend some time and effort up front to make sure that new features and small-scale redesigns won't break old code has really clarified my thinking about system design. Taking it for granted that no one really cares if things are broken for a bit while you refactor really presumes a lot about the problem domain you're working in.
The quoted text doesn't make it clear at what point they think it's OK to see this newly broken code. For example, do they mean in the shared development branch? Certainly you wouldn't want to push it to production, right?
Personally, I break things in my local branches but make sure everything that leaves my machine is working. Git makes it pretty simple to create "save points" when refactoring and then smash everything back together into a clean, concise commit log.
I think the idea of things being broken for a bit while you refactor implies that you haven't checked the code in, not that you don't care about breaking things in production.
I've been working with some pretty bad programmers lately, roughly sub-fizzbuzz or close to it, and one of the most stunning differences I've noticed is how terrified they are of their code. They program like they're building a house of cards. Once it seems to work, the idea of refactoring it for readability is completely foreign to them (of course, so is the idea of unit testing).
I think it's that kind of programmer this article is aimed at. Sure, you should make sure you don't break things, but you should also have some confidence in your code and be willing to constantly improve it.
"I think the idea of things being broken for a bit while you refactor implies that you haven't checked the code in..."
This makes me cringe. I think it means that the code hasn't been merged with the main branch, or to the release branch, or that the feature flags which turn on the new code haven't yet been set to turn it on by default yet.
> an environment where no one was really responsible for making sure that features worked consistently
Programming is a relatively well understood domain (contrary to popular belief). Management is the weak point in today's software production and maintenance. Lack of effective management distinguishes software development from other engineering disciplines.
I disagree. From personal experience when working with production systems fear is usually much more prevalent. Often a refactor will bring about benefits that are only realized by the dev team. The business side doesn't really care that you just removed duplicate code/added technology x. But they will notice immediately when something breaks. This leads to a high risk low reward environment where "dont fix it if it aint broke" is a viable survival strategy.
In cases like this I think the advice is extremely solid.
There's a tradeoff. It's important to approach rewrites and even legitimate breaking changes with the proper degree of maturity. A cavalier attitude to code quality is unhelpful and unprofessional, but gold plating a system out of a fear of change is almost as bad.
I'd say the advice in the article is of mixed usefulness. Rewrites are serious business and merely embracing the idea that they are possible is not nearly as important as acquiring the skills, tools, and best-practices to do such things effectively.
The fundamental problem that must be wrestled with when it comes to making big changes (major refactorings, rewrites, etc.) is not breaking things, it's risk. Everything about dealing with big changes is about risk management and risk mitigation.
There are lots of ways to deal with making big changes though, here's a few bits of advice quickly:
1: Don't necessarily commit to the change prematurely. More so the larger the change. If you need to rewrite a complete application, system, or sub-system then consider keeping the new and old system in parallel. At the product level consider releasing a new product to compete with the old. It's easy to discount the value of maturity in a product but the value of real-world testing is immense even though often times its impact on a code base may not be incredibly obvious, especially if there are fundamental flaws which are evident. As an example, consider Microsoft Windows, which released NT-kernel and 9x-kernel versions of their OSes over a period of more than half a decade, allowing them to compete against each other and allowing the newer system to attain maturity before finally discontinuing OSes based on the old kernel.
2: Consider allowing breaking changes to be released. This is a fundamental tool that is necessary for anyone developing systems code whether it's kernels, servers, services, libraries, frameworks, etc. This is why it's important to version things, because you want to make sure that people have the choice to take an upgrade that includes a breaking change or not.
3: Get acquainted with all of the various techniques and tricks to deal with different forms of versioning. Library/framework versioning. API versioning. Service versioning. And make use of the tools and techniques which serve your use cases the best. For example, sometimes you need to completely change the way some functionality works which would result in client code having to be completely rewritten. In that case it usually makes sense to just introduce new functionality that just exists in parallel with the old ways of doing things rather than trying to press the new functionality into a form similar to the old or having to worry about versioning schemes and such-like. Also consider techniques like "feature flags" and staged rollouts of changes (every big software company from MS to Amazon to Google does this sort of thing and has built lots of tooling to support it).
4: Get skilled at refactoring, increase test coverage, and work step by step.
5: Formulate a development plan, the bigger the change the more formal the plan should be. Don't underestimate the effort necessary and don't skimp on things like QA. Also, have a backup plan in case the change does not go as planned. This is why it's often important to avoid premature commitment and to continue maintaining the old code, which you should set aside resources for as a risk mitigation technique depending on the nature and size of the change.
6: Read "Working Effectively with Legacy Code" by Michael Feathers and make use of the techniques described.
As a side note, I was very impressed with the ReadSpeaker solution they implemented when you select a paragraph of text. Didn't know that product/company.
17 comments
[ 3.2 ms ] story [ 46.1 ms ] threadFor me, the challenge is to look at older technologies as often as newer ones. I don't quite believe that everything new was old at some point, but it is rather impressive to see the tricks and techniques that were used by folks years ago. To the point where I'm sometimes curious exactly what we have that is truly new. (I'm starting to learn just how powerful emacs can be, for example.)
Also, another person can recommend you a tool, library, etc which you wouldn't have thought of. For instance, at my early days as a developer I didn't know that things like SQL schema version control exists. I got introduced to liquibase which saved me from many many hours of work making sure all environments are using the same schema.
It all depends on who sees your code and the way they approach with comments, and how do you deal with criticism.
I was formerly working in an environment where no one was really responsible for making sure that features worked consistently or that new features didn't incidentally break old features, and the result was a huge amount of frustration and wasted time. Learning to spend some time and effort up front to make sure that new features and small-scale redesigns won't break old code has really clarified my thinking about system design. Taking it for granted that no one really cares if things are broken for a bit while you refactor really presumes a lot about the problem domain you're working in.
Personally, I break things in my local branches but make sure everything that leaves my machine is working. Git makes it pretty simple to create "save points" when refactoring and then smash everything back together into a clean, concise commit log.
I've been working with some pretty bad programmers lately, roughly sub-fizzbuzz or close to it, and one of the most stunning differences I've noticed is how terrified they are of their code. They program like they're building a house of cards. Once it seems to work, the idea of refactoring it for readability is completely foreign to them (of course, so is the idea of unit testing).
I think it's that kind of programmer this article is aimed at. Sure, you should make sure you don't break things, but you should also have some confidence in your code and be willing to constantly improve it.
This makes me cringe. I think it means that the code hasn't been merged with the main branch, or to the release branch, or that the feature flags which turn on the new code haven't yet been set to turn it on by default yet.
Programming is a relatively well understood domain (contrary to popular belief). Management is the weak point in today's software production and maintenance. Lack of effective management distinguishes software development from other engineering disciplines.
In cases like this I think the advice is extremely solid.
I'd say the advice in the article is of mixed usefulness. Rewrites are serious business and merely embracing the idea that they are possible is not nearly as important as acquiring the skills, tools, and best-practices to do such things effectively.
The fundamental problem that must be wrestled with when it comes to making big changes (major refactorings, rewrites, etc.) is not breaking things, it's risk. Everything about dealing with big changes is about risk management and risk mitigation.
There are lots of ways to deal with making big changes though, here's a few bits of advice quickly:
1: Don't necessarily commit to the change prematurely. More so the larger the change. If you need to rewrite a complete application, system, or sub-system then consider keeping the new and old system in parallel. At the product level consider releasing a new product to compete with the old. It's easy to discount the value of maturity in a product but the value of real-world testing is immense even though often times its impact on a code base may not be incredibly obvious, especially if there are fundamental flaws which are evident. As an example, consider Microsoft Windows, which released NT-kernel and 9x-kernel versions of their OSes over a period of more than half a decade, allowing them to compete against each other and allowing the newer system to attain maturity before finally discontinuing OSes based on the old kernel.
2: Consider allowing breaking changes to be released. This is a fundamental tool that is necessary for anyone developing systems code whether it's kernels, servers, services, libraries, frameworks, etc. This is why it's important to version things, because you want to make sure that people have the choice to take an upgrade that includes a breaking change or not.
3: Get acquainted with all of the various techniques and tricks to deal with different forms of versioning. Library/framework versioning. API versioning. Service versioning. And make use of the tools and techniques which serve your use cases the best. For example, sometimes you need to completely change the way some functionality works which would result in client code having to be completely rewritten. In that case it usually makes sense to just introduce new functionality that just exists in parallel with the old ways of doing things rather than trying to press the new functionality into a form similar to the old or having to worry about versioning schemes and such-like. Also consider techniques like "feature flags" and staged rollouts of changes (every big software company from MS to Amazon to Google does this sort of thing and has built lots of tooling to support it).
4: Get skilled at refactoring, increase test coverage, and work step by step.
5: Formulate a development plan, the bigger the change the more formal the plan should be. Don't underestimate the effort necessary and don't skimp on things like QA. Also, have a backup plan in case the change does not go as planned. This is why it's often important to avoid premature commitment and to continue maintaining the old code, which you should set aside resources for as a risk mitigation technique depending on the nature and size of the change.
6: Read "Working Effectively with Legacy Code" by Michael Feathers and make use of the techniques described.
The last time I broke things, I got fired. Not gonna happen again.