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.