Hacker News new | ask | show | jobs
by greatzebu 4552 days ago
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.

5 comments

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.

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.

> 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.

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.

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.