1

In my workplace, the client is obsessed with line-by-line code reuse. And they use a tool that uses LoC as measure of code re-usability.

Often for two modules, if the logic is same as of now, those modules call the same method. Which reduces no. of lines, but seems like a long-term manageability disaster.

Are my concerns valid? Or I'm missing something here?

Not a duplicate of this question, I am worried about plain LoC based measure. I understand breaking it down to smaller component, but it's not that.

5

Code reuse and Lines of Code (LoC) counting are different obsessions.

Both are things to be sensitive to. Neither should be the primary concern.

Good names, readability, semantics, organization, abstraction. I'll take any one of those over these other two.

Code reuse is very good to apply when you find yourself branching every possible case into it's own section of nearly identical code. Separate what varies from what stays the same. Stuff decisions into variables. Code reuse isn't so good if every time you write a line of code you're required to search the code base to be sure there isn't an identical line some where else. Code reuse is much more a conceptual thing than a mechanical thing. I would be wary of tools that tried to do this work for me.

Lines of code is a bottom-of-the-barrel concern. You can do a few good things trying to keep lines of code down. You can do horrible things trying to keep them up. Again this is ignoring the experience of reading the code and focusing on a mechanical aspect. Just prefer adding features to lines of code and you should find yourself keeping methods and classes small.

In short rather then run a tool, I'd much rather do a code review by grabbing a fellow coder for 5 minutes and see for myself how much of what I wrote is understandable.

1

I would actually say that in some cases, it might be better to not obsess about code reuse, and instead focus on writing the code as simply as possible. Too much complex features like subclassing, abstract interfaces that you implement everywhere, etc. mean that it's almost impossible to do any useful debugging. Too much abstraction may also be the reason behind performance problems.

Quick: what is the real data type of the object you have, and what does it do when you call a particular method? The trouble is, you may not necessarily know. And in cases where you may know, it may take a lot of time to gain that information.

If the natural solution for a problem is an interface that you implement, please do use interfaces. Don't put them everywhere, however, just to keep your code line count low. Subclassing might be a natural solutions sometimes, but more often than not you should prefer composition over inheritance and interfaces might be a better idea than subclasses where that preference rule doesn't apply.

I have worked in a project where we implemented a rather complex online service using simple code. Not shared-everywhere code. Not trying to keep the LoC down. Just simple code, and a lot of it. Writing new code was fast: whereas an experienced programmer can write 1000 lines of complex code per month, we could write over 10000 lines of simple code per month per developer. Debugging was a total joy. You could almost always see what a given piece of code does, because not everything was abstracted away behind an interface. SQL queries were manually constructed, so you could easily test their performance on the database and see the query plan by using EXPLAIN. Did we share code? Of course we did, we had some shared code, but used it only when it made sense, not trying to keep the LoC down at all costs. We also used external libraries wherever it made sense. A lot of times, it actually made sense to implement the functionality yourself instead of choosing an external library for a 100-LoC job.

I would recommend that if you want to keep your LoC down, you should create two measures: the LoC of the parts you have written, and the LoC of everything your software depends on. Consider that there's 1 bug per 100-10000 LoC (perhaps 1 bug per 100 LoC for complex code, 1 bug per 10000 LoC for simple code). So, if you have two million LoC in external components, how many bugs do you have? Two hundred? Two thousand? Twenty thousand? Just for the fun of it, try to see whether the external code is simple or complex by looking at it. I would bet it's complex, not simple.

  • Interfaces are a form of inheritance, and inheritance is a form of composition. Using base classes instead of interfaces tends to promote what is typically called composition over inheritance, because in those languages which have interfaces, you can only inherit from one base class. That means that child instances implement additional functionality, instead of being bundled into one class implementing many interfaces. I understand what you are trying to say, but it may not be the best expression. – Frank Hileman Jul 19 '17 at 15:57
  • 2
    "Using base classes instead of interfaces tends to promote what is typically called composition over inheritance" - This makes no sense at all. – Sebastian Redl Jul 20 '17 at 11:56
  • "Interfaces are a form of inheritance" - Not really. All classes have an interface whether a language makes it explicit or not. An interface is a description of a class' usage from the outside. Many statically typed languages include a mechanism to explicitly separate a class' interface from its implementation for interchangeability but classes which don't use those have an interface nonetheless. Dynamic languages do this implicitly by allowing all classes to be interchangeable for better or worse. Inheritance on the other hand is about both implementation reuse and interchangeability. – Ben Cottrell Jul 21 '17 at 7:53

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.