66

At some point a program is in development. Features are being added or removed or changed all the time. Every version is nothing but a prototype. So I don't waste much time on writing super clean code at that point because I never know how long something lasts. Of course I try to keep the code quality to certain standards, but time is always an issue.

Then comes the point where the program is finished and the decision maker(s) say "that's it". I do have a working prototype at this point, but the code inside is a bit messy from all the back and forth during the development phase. I am expected to start testing/final debugging but my gut says I should now somehow clean up and or rewrite stuff to give it proper architecture that makes maintenance etc easier.

Once stuff has been tested and approved, it makes no sense to rewrite then. On a regular basis I am standing there with a working 'finished' prototype and I get a bug during testing and I see that it is a result of not-smart coding which is a result of the whole development process. I am in the middle of testing and the bugfix would be a rewrite... it's a mess!

There are better/textbook ways, I am sure. But i have to work in a real work environment where not everything is textbook.

So how do I transition my working prototype to a release version with a stable code base ? Maybe I should not consider the development finished once I do and actually see it as the clean-up phase... I don't know, I need help here.

EDIT

I want to clarify a few things.

  • I am 100% on the side of doing it right before and not after, code clean and readable. But i also have to get things done and can't dream about the beauty of code all clean and shiny. I have to find a compromise.

  • often a new feature is really just something that we want to try out and see if it makes sense to implement something like this. (esp. in mobile apps, to get a real look-and-feel on an actual device) So it is something small that (imho) does not justify too much work in a first "let's see" iteration. However sometimes the question arises WHEN do i pay this tech.debt ? That's what this question is all about.

If I know that half of the features will be dropped one day later (enough experience in our company by now) I really find it hard to believe that the best way to approach my problem is to nonetheless invest extra time to write everything clean even if most of it will be dropped shortly after. It feels to me that I will save time if I do one big cleanup once the thing is solid, hence my question.

15
  • 68
    Your question is "I dug myself into a hole; how do I get out?" The standard answer is of course step one, STOP DIGGING DEEPER. Your development process can be summed up as "generate enormous amounts of technical debt and then ignore it when it comes due". If this is a problem, change your development processes. Only check in clean, working, debugged, carefully reviewed code that meets its carefully written specification. Don't get into debt and you won't have to get out of debt. Commented Mar 9, 2016 at 15:11
  • 11
    @NikkyD If you are not given the time for proper implementation, you need to have a conversation with your manager about the impact on the quality of the software. Understand what everyone here is telling you: not investing the time up front is damaging your ability to work efficiently later. Another issue you want to bring up with them: if you were to leave the company (or "get run over by a bus"), it would be extremely expensive for new developers to familiarize themselves with the code. The money they think they are saving now will cost them later.
    – jpmc26
    Commented Mar 9, 2016 at 17:28
  • 32
    If you are making a little feature branch to mock up a proposed user interface for a feature, that's great. Make that as quick and dirty as you like, show it to the client, and then delete that branch. When automakers make cars out of clay and paper to mock up a new design, they don't then try to put an engine in the clay model. The process to determine if the feature is worth doing should be cheap. Once you've decided to do the feature, make sure you are starting from clean code and always producing clean code, because that code is now production code. Commented Mar 9, 2016 at 19:02
  • 10
    "can't dream about the beauty of code all clean and shiny" This is a fundamental misunderstanding of what "clean code" means. Clean code does not mean you spend an afternoon making your tabs line up so you can print the code and frame it. Clean code is good code and good code is clean code. Clean code is code that works properly, can be debugged, and can be understood. If you don't have time to write clean code from the beginning, then you definitely don't have time to write messy code and then also fix it later. That's just how long the task takes. Commented Mar 9, 2016 at 22:12
  • 8
    "I also have to get things done and can't dream about the beauty of code all clean and shiny. I have to find a compromise." A compromise means a middle ground that's "good enough" for both sides. If your code is messy - particularly if it's messy enough that you think you'll have trouble maintaining it - then that's not "good enough", and you need to find a better compromise. Commented Mar 10, 2016 at 12:01

7 Answers 7

97

So I don't waste much time on writing super clean code at that point because I never know how long something lasts.

Not knowing how long something lasts should never be an excuse for sloppiness - quite the opposite. The cleanest code is IMHO the one which does not come into your way when you have to change something. So my recommendation is: always try to write the cleanest code you can - especially when coding a prototype. Because it will be much easier to adapt it when something has to be changed (which surely will happen).

Don't get me wrong - my understanding of "the cleanest code" has nothing to do with making code beautiful for the sake of beauty. That is indeed something which can slow you down. In my point of view, clean code is code which is mostly self-explaining (no need to write so much docs - causes speedup), easy to understand (less errors, so less debugging needed - speedup, less time needed to find the correct place to alter - speedup), solves the given problem with the least amount of necessary code (less code to debug - obvious speedup), is DRY (only one place to change when something has to be changed - speedup - and less risk to introduce new bugs by forgetting to change a second place), follows coding standards (less cumbersome things to think about - speedup), uses small, reusable building blocks (which can be reused for many features or even prototypes - speedup), and so on.

I am expected to start testing/final debugging but my gut says I should now somehow clean up and or rewrite stuff to give it proper architecture that makes maintenance etc easier

Doing "cleanup" afterwards never works. Consider you cleanup before you implement a new feature, or when starting to implement it, but not afterwards. For example, whenever you start to touch a method for a feature, and you notice it gets longer than 10 lines, consider to refactor it into smaller methods - immediately, before getting the feature complete. Whenever you detect an existing variable or function name you do not know exactly what it means, find out what it is good for and rename the thing before doing anything else. If you do this regularly, you keep your code at least in a "clean enough" state. And you start saving time - because you need much less time for debugging.

I am in the middle of testing and the bug fix would be a rewrite

... which is the actual proof for what I wrote above: being "dirty" haunts immediately back on you when you start debugging your code and will make you slower.

You can avoid this almost completely if you do the cleanup immediately. Then bug fixes will mostly mean small changes to the code, but never a major architectural change. If you really detect evidence for an architectural improvement during testing, delay it, put it into your issue tracking system, and implement it the next time you have to implement a feature which benefits from that change (before you start with that feature).

This takes some discipline, and some coding experience, of course. It is a similar idea like the idea behind "test driven development", doing these things beforehand instead of doing them afterwards (TDD can help, too, but what I wrote works even when you do not use TDD). When you do this consequently, you will not need any special "clean-up phase" before releasing.

14
  • 40
    @NikkyD The suggestions made by Doc Brown are habits which actually decrease time taken and are very realistic in the long run. Think how much time you save if you don't need to examine your code to figure out how to not break it every time you need to change it. The gains are similar to changing from "hunt and peck" typing to learning to touch type. It may take longer initially, as you are learning, but once the habit is there it is undeniably better and will benefit you for the rest of your career. If you choose to not try, you will never get there.
    – Daniel
    Commented Mar 9, 2016 at 14:50
  • 44
    @NikkyD: It does not bloat the timeframe. The timeframe was already bloated; you just didn't account for the bloat when you wrote the software and got into technical debt that you hadn't budgeted for. Commented Mar 9, 2016 at 15:13
  • 7
    @NikkyD: I present you the Idol with Feet of Clay and the concept of Technical Debt. The former means that you can build sound software on shaky foundations, the latter is about the "interested" (added cost) experienced by features that you try to tack on when the structure is not sound. Commented Mar 9, 2016 at 15:14
  • 10
    @NikkyD: no, I suggest to write your code similar to how a billiard expert plays his balls: each shot looks simple for the outsider, since after the shot the balls stop in position for a new "simple shot". And in billiard or coding, this takes some years of practicing ;-)
    – Doc Brown
    Commented Mar 9, 2016 at 16:11
  • 16
    @NikkyD: to my experience, when "adding a little feature requires a lot of refactoring" the code is already a mess, and the need for "lot of refactoring" comes from the fact you need to change a function or class which you did not keep clean enough in the past. Don't let things get so far. But if you are in that situation, find a compromise. At least follow "the boyscout rule" and leave the code behind in a better state than it was before you added the feature. So even if the feature got removed next week, the code should be in better shape than it was before.
    – Doc Brown
    Commented Mar 9, 2016 at 20:10
22

You have two separate problems, both with the same symptom (sloppy code):

Problem #1: Insufficient requirements control I don't mean that your stakeholders change your requirements too frequently, I mean that you're allowing requirements changes during a bugfix/test cycle. Even the agile methodologies don't support that; you build, you test, you deliver, you inject new requirements.

Problem #2: You believe the stuff you're writing is "just for now" In software development "just for now" code is really extremely rare. You've noticed yourself that once you've satisfied a user requirement, the rigors of supply and demand make it very difficult to justify going back and re-implementing a "done" feature. So, what to do about it? Always write production code. Functionally for you, that means your estimates to your stakeholders need to be substantially bigger so you've got some time to do it right.

Also, please understand that you're working in the most difficult position as a developer: Read Joel Spolsky's take on the life of an in-house developer. So, you need to be extra-vigilant if you want to come through with your sanity intact.

0
21

It is a common problem - especially when crafting what is essentially a software trial balloon as it were.

There are a number of approaches that can help. Firstly TDD approach can help to reduce the code base to that which is strictly required. If your tests go hand in hand with your code, then you can at least have some confidence that your code behaves as it should.

Take time to refactor as you go. Once you have a prototype and the customer is super-eager to get their hands on it, it is a difficult sell to say you need time to polish what (to them) is complete. I like to check in on a daily basis followed by a refactor check in but YMMV.

Developers who write code quickly are often in demand - we had such a developer in my last department. Every team wanted him because he worked super fast. Once the time came to test and release his code however, the wheels quickly came off. Hard coded stuff, hacks and shortcuts everywhere. His stock soon fell - massively.

Cutting production code from the outset can seem like a drag but depending on your environment, there are many tools that can take the grind out of developing such as Ghostdoc and Stylecop.

It is worth getting in the right development mindset from the outset. You'd be surprised how many back-of-a-fag-packet systems that were supposed to be just stop-gap solutions become cornerstone applications.

2
  • You mean every stop gap solution ever written, right?
    – RubberDuck
    Commented Mar 10, 2016 at 23:15
  • 4
    A great point about justification to the customer. I have plenty of experience with customers who think that when the GUI looks done, the application is done as well. I learned to make the GUI look incomplete while the code in the background isn't ready yet, and consequently, only make what the customer sees look polished when the code (and business logic) is. It's very hard to explain that what looks finished to the customer will still take a month or two to actually deliver.
    – Luaan
    Commented Mar 11, 2016 at 9:57
11

Continuously

Speed of development is the main reason to write clean, readable and testable code; it's not done for beauty, nor other abstract values. Why would I deny that to myself and only do it afterwards for some future programmer?

Sure there might be changes that are mostly cosmetic and therefore not essential; I'd argue that it's far more useful to have moderately nice code right now, during development, than having a mess right now and hoping to make it perfect later (which, let's face it, it's never going to happen, even if you had the time).

5
  • 6
    +1 and from a personal point of view, I found it too difficult to change gears from hacking personal projects at home and writing production code in my day job. Writing professional code in my hobby projects paid immediate dividends - code was easier to read and there were far fewer bugs.
    – Robbie Dee
    Commented Mar 10, 2016 at 12:37
  • One reason it's never going to happen is that you (better) get better at what you do over time. So if you wait for half a year to "clean up" something, you'll not only forget all the minutae required to safely do the clean up, you'll also be a better programmer than before, and you'll likely be tempted to just throw everything away and start over. And since that's so much work (and often a bad idea anyway), you're probably just going to skip the cleanup again.
    – Luaan
    Commented Mar 11, 2016 at 9:59
  • " Why would I deny that to myself and only do it afterwards for some future programmer?" Revelation! And, guess what? You sometimes (and sometimes, often) are that future programmer.
    – radarbob
    Commented May 7, 2016 at 22:16
  • @RobbieDee, Superlative observation! In an interview Malcom Gladwell - the guy who brought the "10,000 hour rule" to popular awareness (in the book Outliers) - said that it must be "deliberate practice" or it is just wasting time. Meaning focus on improvement, practice specifics with the intent to improve that specific aspect of a skill, etc.
    – radarbob
    Commented May 7, 2016 at 22:24
  • @ThanosTintinidis, then there is the "no good deed goes unpunished" problem. Having written such clean code, someone will inevitably bollocks it up. Make sure you're the code reviewer when someone else touches your clean code. One simple added method blew the encapsulation and coherence that was even documented in-line. I was furious for a week; and a year later, every time I see that code.
    – radarbob
    Commented May 7, 2016 at 22:34
4

You do this by differentiating between "I am just trying this to see how it works" code and "this is headed into the product" code. There are a number of ways to do it.

One is branching or whatever the word is in your source control system. You make a branch for the new report or the new import layout or whatever. If people like it, the job of getting it back into the main branch is a separate, trackable job. It can be assigned to someone and reported on and isn't expected to just magically happen the day management (or sales) agrees that the feature belongs in the product.

Another is spikes. You don't do that change in the product. You go off into some separate app, super simple, that exists only for you to have a place to put code. You can be as messy as you like because you're just exploring the new API or whatever. And again, if you come back and report "yes, we can do that, I've figured out how" there is a trackable, reportable, assignable task of writing product-ready code in the product to do what you want.

In both cases, product-ready means readable, neat, following naming standards, with tests, and adhering to your code style and performance targets. In both cases, you make that work visible. I agree that you don't want to do all that work every time when someone is quite likely to yank the feature back out of the product. But you don't want to let that work get invisible either. Working in separate copies of the product or in an unrelated product that's barely more than a test harness allow you to surface the work to make product-ready code once someone decides they want something.

The downside is they can't decide they want something and ship it (meaning the half-assed, messy, untested, undocumented, possibly slow half-version that you have implemented as a proof of concept) tomorrow. They first time you get pushback on that front, simply ask if you should do it the long (more expensive) way every time just in case, slowing down the path to rejected features. If you ask correctly, you will get a "no".

1

Really I think you understand the problem already. The problem is that your coding style is requiring you to do too much rework. The reason it is necessitating too much rework is because (a) it is put together with insufficient foresight and planning and (b) the incremental short term patches regularly put in during development combinatorially increase the complexity of any rework required.

The answer therefore, is to

(a) shift your development style a bit more towards waterfall and a bit less agile. Don't go all the way though, because classic waterfall has it's own pitfalls. There is a healthy balance to be had. I know it can be concerning just thinking about stuff for a few days sometimes, like no development is getting done, but you have to trust the process. In engineering you can't just nail things together and then nail things on top and hope to come out with an elegant solution. If there's no-one doing architecture and higher level technical design, that means its your job. You have been paying the price of neglecting that job.

(b) try to avoid patching things over. Don't think long term only when it comes time to do QA. Really you should be testing every little piece you build, all the time, and covering all input cases, those not on the happy-path also. A patch/hack is almost by definition a short term fix, which may well have a long term cost, hits the clients Total Cost of Ownership in the system. Again, the pressure's on to get code out, so there has to be balance. But try not to put short-term fixes in place, esp. those that tightly couple components that should really be loosely coupled. There will be re-work, so do it EARLY to make it much easier, to avoid the hacks and patches that will mount up over time and become unmanageable.

9
  • 2
    Just a note - agile doesn't mean "frequent changes without thought" or "less design". In fact, I find that agile requires far more design than what people are commonly calling waterfall. The lack of good design is one of the reasons waterfall doesn't work well in practice - if you actually invest in the design properly, it works just fine; it just also gets much more expensive than agile. If you skip the design part in agile, you're just banging code together willy-nilly, and that's not going to work any better than any other practice that avoids design.
    – Luaan
    Commented Mar 11, 2016 at 10:02
  • The agile focus on short iterations, sprints etc, getting prototypes out fast, necessarily puts more pressure on neglecting sufficient design up front Commented Mar 11, 2016 at 13:28
  • No, it puts focus on designing smaller parts. But overall, you must design a lot, otherwise you're just going to produce a horrible product. The key is to make things small and well designed, as well interchangeable. If you design less in agile, you're doing yourself (and your customers) a disservice. Short iterations are the benefit of using agile, not the pre-requisite or just part of the process - once you get everything good enough, you can suddenly afford the short iterations, not the other way around.
    – Luaan
    Commented Mar 11, 2016 at 13:53
  • Putting more emphasis on designing smaller parts is a major problem, when really the bigger picture is commonly the most significant cause of re-work. I've seen much more money wasted on the business all-of-a-sudden saying "but we need it to do this", that requires a wide-ranging overhaul than I've ever seen in the change of a design of a small loosely coupled component Commented Mar 11, 2016 at 14:14
  • Yes, but by that point you've already lost (and regardless of whether you were trying for agile or waterfall). When your "big picture" is composed of many small parts that are relatively isolated, the only wide-ranging overhaul you get is when you need to replace pretty much everything. What approach doesn't make you lose everything when you need to start from scratch? Even NASA levels of design lead to once-in-a-while "we need to change everything". By keeping yourself flexible and adaptable, you get more maneuvering space for accomodating changes, small or big.
    – Luaan
    Commented Mar 11, 2016 at 14:17
0

You write:

Every version is nothing but a prototype. So I don't waste much time on writing super clean code at that point because I never know how long something lasts. ...

Then comes the point where the program is finished and the decision maker(s) say "that's it". I do have a working prototype at this point, but the code inside is a bit messy from all the back and forth during the development phase.

A checked-in version can be a "prototype" in that it misses features or some features are not fleshed out, but all the code checked in should be production quality code that does not neccessarily need clean up.

I think you're postponing you "cleanup" to much.

My rule of thumb is:

  • Start with (sub-)feature
  • feel free to write incomplete and inclomplete stuff, maybe some c&p to get a feel for what I'm implementing or if I have to scratch the last hour(s) of coding (note that this can go hand in hand with TDD / tests, it's just that everything is toned down a bit to get quick feedback of the implementation space I'm exploring)
  • Sub-feature "works" good enough for now
  • Now do the clean up: prior to a SCC commit.
    • Look over the code to see what's obvious
    • Do a diff vs last commit to review changes and maybe catch some problems
    • Fix stuff I noted down on my scratchpad
  • Now I do the commit -- this code quality is ready to ship

At this point, the committed code may still contain some workarounds or "technical debt" that it would be nice to clean up, and maybe I'll clean it up when it's the natural thing to do for a following sub-feature, but it will be OK if that code is released as is.

Not the answer you're looking for? Browse other questions tagged or ask your own question.