“What does this ‘refactor’ word you keep using really mean . . .?”
I could almost hear the client’s eyes and nose scrunching up in scrutiny. We were talking about realistic budgets and schedules for a potentially large, multi-developer feature addition to his existing application.
I switched gears, put on my imaginary professor glasses and set my imaginary apple on the corner of my desk.
“Well, you are right. It’s part of the software development and developers’ lexicon. It’s a word that means – sort of – ‘optimize’ or ‘clean up’ or ‘tighten’ and, really, all three of these things at once.”
“So, the code is dirty?” he intoned. I could hear the silent thought bubble form above his head — “but-I’m-paying-you-NOT-to-write-bad-code!”
I’m as practiced in this discussion now as I once was practiced at explaining why bugs are a natural and expected part of software development and not, as some think, evidence of poor code quality or shoddy development practices, and that, because of this, bug-fixing is billable work. That concern doesn’t seem to be an issue with clients of late. But the idea that codebases need to be refactored remains.
Refactoring is necessary. Especially on, though not limited to, large or complex projects developed over an extended period of time (say, more than 4 months). To understand refactoring, you must understand a few core concepts about software development:
It is a collaborative endeavor involving many technical roles (developers, testers, designers, database architects) and multiple business roles (the users, the project managers, client stakeholders, product managers, etc). A software project does not come from a single “pen” but from multiple authors, all writing the same book.
A custom software project isn’t a product. It’s not a widget whose assembly has been mapped, measured and then put into mass production with each version-stamped out nearly perfectly for consumption. It is, always, the creation of the FIRST widget. It’s a one-of-a-kind creative effort . . . even the ones that are “just like” something else.
Software development does not exist in a vacuum. It’s not a theoretical exercise and therefore outside pressures of schedule, cost, conflicting requirements, conflicting opinions, undefined features, and more exert influences on what is implemented and how. Software will never be perfect and it will always reflect the myriad compromises made during its development. Some of these compromises are benign or even advantageous and some are inefficient and incur “technical debt.”
If the above seems intuitive to you, then the reason for refactoring a code base periodically is evident. We refactor things in our daily lives all of the time to smooth out our experience, optimize our workflow . . . make things better. We just don’t call it refactoring. It’s all of those things that you do once and, although you’ve successfully achieved your goal, you return to refine so that you can obtain better results. It’s the editing you do on the first draft of an email; the tweak to your route to work that avoids the intersection that takes forever; the shuffling of responsibilities and due dates to ensure better presentations and proposals. It’s the rumination you do in the car after an argument with your spouse in which you surely made your point but not in the most elegant way possible. It’s the knowledge, in fact, that you are fairly certain you should apologize and reframe things more accurately or you’ll probably hear about it again. In a bigger way.
It’s exactly like that.
When a developer refactors code, he or she is looking for those tiny rifts (or not so tiny) that are the result of multiple people working on the application simultaneously or implementation approaches that seemed correct at the time but, give current feature specifications and functionality, have become sub-optimal. They are looking for places where compromises were made due to schedule or to support (sort of) two conflicting requirements that have now, through testing, been solidified. They are looking for places where the screws are still a bit loose on the application and they tighten them down so that they will better withstand the load of production. They are looking for things that may potentially throw bugs. They are looking for issues that may arise if, in the future, the application is extended. And where they can’t tighten, clean up, or fix, they are commenting and documenting the code better so that the next person in the codebase knows what’s happening — especially around complex business logic or wonky, but necessary, implementations.
It’s good code hygiene.
Okay, so good code hygiene . . . but why touch something that ain’t broke? Why spend the money to refactor? Isn’t that something you could and should skip when things get down to brass tacks?
Yes and no. And by that I mean, mostly, no.
When you don’t refactor, small rifts tend to grow into big chasms that can pull an application apart. Okay, maybe that’s a little too melodramatic. But they will cost you money down the line and probably more than if you just addressed them now. You can choose to kick that can down the road or, if you have the flexibility of schedule and budget, you can deal with it now. Your choice. It’s a valid business decision to make. Unrefactored code is more difficult to support and maintain. New development on top of an existing unrefactored codebase can be more difficult. Debugging can be harder and the bugs more difficult to fix. In short, your application becomes a PITA to work with. In extreme cases, unrefactored code becomes a mountain of technical debt that requires ripping out whole sections of the application and reimplementing them so that new features can be supported.
Pay me now or pay me later.
Your developer and project manager will help guide you with this decision and when and where it makes sense to refactor your code. If they are suggesting a code refactor, in sum or in part, it’s important to take the suggestion seriously and ask for details about the objective and need for the refactor.
My client chose to leave the codebase ‘dirty.’ Once. As James Russell Lowell once quipped, “One thorn of experience is worth a whole wilderness of warning.”
Spot the Vulnerability: Loops and Terminating Conditions
Spot the Vulnerability: Loops and Terminating Conditions In memory-unsafe languages like C, special care must be taken when copying untrusted data, particularly when copying it to another buffer. In this post, we\'ll spot and mitigate a past vulnerability in Linux\'s...