6

Are there any tips that can help me create clean code when I'm working with something poorly documented and completely new to me?


It is easy to write a clean code when we are writing something for the second time. However usually problems we are approaching are new to us.

Let's say we're working with a poorly documented library: we have created our first, well-designed implementation, but after compiling it appears it doesn't work. We find some quick fix on the forum and paste it. It still doesn't work, so we are quickly searching for another code snippet to paste. Some time after our clean code is full of quick solutions taken from the Internet.

In the end, our program starts working. We look again at our code and it looks obnoxious - one big procedure full of "temporary" solutions we applied while trying to make it work. We've got two choices: rewrite everything from the scratch hoping that refactored code will work, or leave it as it is since it is working. In most companies, the second approach is taken, at least until more tutorials for the library are released.

Sometimes we don't have a possibility to debug every single line we're adding. Also when we are working with the badly documented library, usually we need to guess which part of code made our program work. I'm having the biggest problems when providing engineering solutions when debugging is really time-consuming, since you need to launch the program on a real device.

  • 3
    You really want to write clean code ? Read this book: Code Complete 2. It's the single best compilation about how to write code I've read so far. When you finish reading, read again, just to be sure. Then you keep it beside your desk for further consulting when necessary. – Machado May 8 '17 at 17:50
  • 2
    Possible duplicate of I've inherited 200K lines of spaghetti code -- what now? – Robert Harvey May 8 '17 at 19:14
  • 2
    If you have this undisciplined style of cowboy coding, your code will end up in a big ball of mud, if your lib is poorly documented or not. – Doc Brown May 8 '17 at 21:30
14

If you do not know how a library works, don't fiddle around with your production code and throw wild guesses of code snippets at it until the code seems to work. This is unprofessional cowboy coding.

Instead, write exploration tests to find out how the lib really works, and when you have enough confidence you understood what happens, then write your production code. This forces you to keep your "temporary" solutions in the tests, and helps you to keep the production code clean. Moreover, try to give your test methods good names and some description of what they are testing, then your tests become the missing documentation in form of examples.

Besides that, I think @DaveGauer's answer contains lots of good advice. You should try to develop an attitude of cleaning up things immediately, before they run out of your control. Separating your "experimental code" from your production code might help you with this.

  • 1
    This is great advice. I do it all the time, but didn't think to mention it: create throw-away explorations but don't make them part of the production code! – DaveGauer May 9 '17 at 16:37
  • 1
    @DaveGauer, except, don't throw them away: Write them as unit tests, and roll them into your test suite. That way, you have documented the features of the library that your code depends on, and might find out sooner if a new version of the library fails to meet your requirements. – Solomon Slow May 15 '17 at 15:17
  • @jameslarge Interesting. I'd have to think about that. Usually the exploration throw-aways contain a lot of floundering and wrong choices. The good stuff ends up in the production code. I would definitely agree that the moment I'm ready to delete the explorations would be the ideal time to write some tests, though! – DaveGauer May 15 '17 at 16:07
  • I'm not saying to keep the mistakes, but keep the ones that shape your decisions about how to use the library. – Solomon Slow May 15 '17 at 16:41
20

First, constantly be re-writing as you go. Don't wait for a big refactor. Don't immediately move on to the next problem when you get one figured out. Pause a moment to reflect on a solution's part of a larger whole.

Second, consider not using using copy and paste quite so much. Certainly, use other people's examples. But type them yourself. Change variable names and code structure to match your project's style. You will break the code because you forgot to rename a variable. It will take you a little longer. But you'll understand what you entered and it will fit your project better and you'll have a much greater sense of ownership for the code.

We've got two choices: rewrite everything from the scratch hoping that refactored code will work, or leave it as it is, since it is working.

You have a third choice. Read the new chunk of code from top to bottom like the chapter of a novel you're writing. Think like a fiction editor. Make changes as you go to improve clarity and check for possible mistakes. Your functions should be small enough that you can reasonably refactor one in a single sitting and understand its entirety.

Technical debt can be a terribly demoralizing thing. I know.

Also, nobody will ever give you time to do the right thing. You have to take the initiative yourself. If you have to, don't let anybody know that you've solved the problem until you've also given yourself time to make the code readable and correct. That goes for testing, too.

  • 7
    "don't let anybody know that you've solved the problem until you've also given yourself time to make the code readable and correct" Yes, I also got the impression that the OP is a young nervous guy who wants to shout "done!" as soon as possible. You should not rush to the exit but rather imagine what it would be like for the next person who needs to apply a change to that code you just ravished. And what he would think of you. If you are not responsible for the mess, the least you can do is not make it worse. – Martin Maat May 8 '17 at 19:32
-3

There are two aspects you can improve without any knowledge about the purpose of the code:

  • apply dependency injection / inversion of control (DI)
  • reduce code duplication

You can do either one relying on the automated refactorings of your IDE.

You should start with DI and then add UnitTests to your application which will help you to understand the business rules in the code and save you from altering the logic in an unwanted way when reducing code duplication.


DI has no value in and of itself; – Frank Hilema

It has as much "value in and off itself" as any other OO principle we follow. That you can't see its "value" doesn't mean there isn't one.


it is only useful when you need it. – Frank Hilema

DI/IoC greatly improves (re-)usability.

And one of the most important cases where this "reuse" is needed is unit testing.

Having DI/IoC applied reduces complexity on the unit tests because in enables the replacement of dependencies by test double.

Also to apply DI/IoC you can follow a formal algorithm. The only thing you have to know is which of the invokation of the new operator instantiates a dependency rather that a Value Object / DTO.

Since the OPs problem is that she doesn't know anything about the she wants to "clean up" this would be my preferred approach:

  • Apply DI/IoC
  • write unit test to verify/understand what the code does.
  • apply other refactorings like extract method, rename identifiers, extract class and others saveguardes by the unit tests written in the previous step.
  • DI has no value in and of itself; it is only useful when you need it. – Frank Hileman May 11 '17 at 1:25
  • It has as much "value in and off itself" as any other OO principle we follow. That you can't see its "value" doesn't mean there isn't one. – Timothy Truckle May 11 '17 at 7:58
  • 1
    Dependency Injection does not tell you in any way how to use the library. DI does not help you write "cleaner" code in any way. Its not the magical soluation that makes all problems go away. – Polygnome May 11 '17 at 10:09
  • "Dependency Injection does not tell you in any way how to use the library." I did not say that. I say that UnitTest tell you how to use the lib. and that DI simplifies UnitTests. – Timothy Truckle May 11 '17 at 11:51
  • DI is not an "OO principle", it is simply a technique. And it usually has negative value, since usually it is not needed. – Frank Hileman May 12 '17 at 20:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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