1

I am reading a book C++ Software Design by Klaus Iglberger. In the book, the author asserts multiple times that recompilation effects of dependent code is extremely bad. For example,

The heart of the problem is the direct dependency of all shape classes and functions on the enumeration. Any change to the enumeration results in a ripple effect that requires the dependent files to be recompiled.

Can someone explain why this recompilation of dependent code is considered very bad? I completely understand that dependencies will cause issues, but that aside, why the specific focus on recompilation effects?

Edit:

More context around the quote.

Note that this addition (addition of new enumeration value) would have an impact not only on the switch statement in the drawAllShapes() function (it is now truly incomplete), but also on all classes derived from Shape (Circle and Square). These classes depend on the enumeration since they depend on the Shape base class and also use the enumeration directly. Therefore, changing the enumeration would result in a recompilation of all your source files.

4
  • 4
    "The heart of the problem is ..." - can this quote be expanded? It sound like he's referring to an aforementioned issue.
    – Martin Ba
    Commented Nov 4, 2022 at 9:14
  • 1
    I have a feeling that Klaus Iglberger is not saying that the recompilation it self is a problem, but more the direct dependencies. What is the problem he is talking about in "The heart of the problem"? Commented Nov 4, 2022 at 9:27
  • @TommyAndersen I think so as well but he explicitly says something along the lines of "will cause recompilation of lot of code" rather than "lot of code will be dependent on each other" many times. That made me think there's a difference between these two .
    – User 10482
    Commented Nov 4, 2022 at 14:17
  • @MartinBa Added some more context in question details. The situation is we currently have few shapes that use enumeration to know their type (eg: square, circle). The quotes refer to the effects of adding of a new enumeration value (eg: traingle).
    – User 10482
    Commented Nov 4, 2022 at 14:21

3 Answers 3

12

Recompiling dependent code is expensive: compiling takes time, particularly in a language as syntactically complex as C++. Historically, for most C++ compilers it was extremely expensive. But more importantly, dependent code might not be under your control. Changes that require recompilation mean that codebases around the world require recompiling when they update your library. This makes upgrading much more difficult than it would be if your new version were binary-compatible.

1
  • 3
    A lot of OOP design guidelines are focused on maintaining ABI compatibility, especially in C++ with patterns like pImpl. That is great advice for libraries. It's often irrelevant in applications or other closed systems (save for C++ compile times). I wish authors would distinguish these cases more clearly.
    – amon
    Commented Nov 4, 2022 at 17:27
4

Not only is recompiling dependent code expensive - right now it might be manageable, but software tends to grow, and when you have millions of lines of code to recompile just due to a little enum getting a new member, you have a problem.

Also, whenever you recompile, there is a chance that this doesn't work - you may want to make a change in the class causing the recompile, and then after a few minutes of compilation there is some compile-time error. Now you have to either change the change, or change something in the dependent class, or rollback everything. You don't need that complexity in your daily life.

Finally, from a high-level design perspective, having avoidable dependencies is a code smell and points to chances to improve your class (module, package, ...) design. A good practice is "high cohesion, low coupling".

High cohesion

A class that has high cohesion takes care of as little responsibilities as possible/useful. This means that the responsibilities of a class do not explode over time and it is always very clear which class is responsible for a given task. Failing to do this leads to a class design with relatively few, huge classes. Maybe not so relevant for your current question, but something to keep in mind.

Low coupling

Any two classes should have as few dependencies on each other as possible. Ideally, changing a class should have little or no effect (recompilation, revalidation, chances to break something, etc.) on any other class, or at least as few as possible. This opens up many good results; for example it becomes much easier to write good tests, you can make changes with much better confidence, and often it is just simpler (less time spent coding); especially for newcomers to the code-base. Good class design, and patterns like dependency injection can help here. Not having to compile another class is a good sign that there is low coupling.

0

First, recompilation isn't as bad as he makes it out to be. Compilers are fast. How often are you going to change that enum?

Second, you figure out what forces recompilations and avoid that kind of dependency, if the compilation time gets annoying. This enum would likely be invisible to anyone outside his "shape" classes, or can be made invisible. Obviously if you can rearrange things to avoid recompilation, then do it.

Third, having dependency upon dependency upon dependency is by itself not clever. If class C can log things, then class B using class C shouldn't be affected by this logging, nor should class A using class B. Recompilation is just a symptom in this case.

6
  • Actual compilation time is the least concern, it's more the fact that the change can force dependant code to be re-compiled before it can function at all, not just to take advantage of the new values or features. Recompiling should also trigger a new wave of testing and review... more time that I didn't want to spend late on a Friday night... Commented Nov 7, 2022 at 16:52
  • @ChrisSchaller If recompiling should trigger a new wave of testing and review, then relinking (and updating dependencies which are dynamically linked) should do that too. Not that automatic recompilation generally causes re-review of the recompiled code. Commented Nov 7, 2022 at 18:38
  • Absolutely @Deduplicator, but the difference is that one becomes optional. But it's not compiler time that is the reason to avoid recompilation, it's the fact that you are forced to recompile at all. The game is to design your dependencies in such a way that they are more abstract, so you don't have to recompile for implementation changes, only for structural changes. Commented Nov 7, 2022 at 21:02
  • @ChrisSchaller A template can be pretty abstract, but any change, even whitespace in a comment, forces all consumers to recompile. Should those consumers really be re-reviewed? Commented Nov 7, 2022 at 21:08
  • If the binary changes then absolutely. A good dependency tree should be flatter to reduce the chance that seemingly insignificant changes force a long chain of recompilation. To complete a review is the only way to verify that a change was actually insignificant, defensive programming 101. Commented Nov 7, 2022 at 21:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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