Based on a LinkedIn post originally published on 9 January 2026
One of the most valuable aspects of configuring Doxygen for a C++ project is its ability to turn a complex codebase into something that can be explored visually.
With an appropriate configuration—and Graphviz available as the graph-rendering engine—Doxygen can generate interactive diagrams covering:
class inheritance;
class collaboration;
function-call relationships;
caller relationships;
include dependencies;
directory structures;
and other connections within the codebase.
When these diagrams are produced as scalable SVG graphics, engineers can zoom into large structures, follow edges between components and move between the visual model and the corresponding documentation.
For medium-sized and large C++ systems, this is much more than a cosmetic feature. It can significantly reduce the time required to understand how the software is organised.
The challenge of understanding an unfamiliar codebase
Reading a new codebase is rarely a linear process.
An engineer may begin with an apparently important class, follow a method into another component, discover several inherited interfaces and then encounter a chain of dependencies crossing multiple libraries.
Even when the source code is well written, the overall structure may remain difficult to see.
Questions arise quickly:
Which classes own the principal responsibilities?
Where are the important abstractions?
Which components depend on one another?
Which relationships are intentional?
Where does inheritance become excessive?
Which classes have accumulated too many collaborators?
Which functions represent important execution paths?
Where has coupling emerged across architectural boundaries?
Source files answer these questions individually. They do not always reveal the complete picture.
A visual representation provides another way of reasoning about the system.
Documentation as a navigation layer
Doxygen is frequently treated as a tool for turning source comments into HTML pages.
That is useful, but it represents only part of its value.
Properly configured documentation can become a navigation layer over the codebase. Instead of reading files in isolation, an engineer can move between:
namespaces;
classes;
inheritance trees;
member functions;
source definitions;
callers and callees;
included headers;
and collaborating components.
This creates several paths through the same system.
A developer investigating behaviour can begin with a class and follow its call relationships. An architect can examine dependencies between components. A new team member can use the hierarchy and collaboration diagrams to develop an initial mental model before reading implementation details.
The source remains authoritative, but the documentation makes it easier to find the relevant source.
Why Graphviz matters
Doxygen can generate several basic diagrams on its own, but Graphviz enables a much richer representation of relationships.
Graphviz takes a graph description—nodes, edges and attributes—and calculates how the information should be laid out visually.
When integrated with Doxygen, it can generate diagrams for:
inheritance relationships;
class collaboration;
function calls;
reverse-call relationships;
include dependencies;
included-by relationships;
directories;
groups;
and graphical class hierarchies.
The result can be produced in SVG format, which is particularly useful for large diagrams.
Unlike a fixed-resolution image, SVG remains sharp while zooming. It can also support clickable links, allowing an engineer to move from a node in the diagram to the corresponding documentation.
That interactivity changes the diagrams from static illustrations into practical exploration tools.
A representative configuration
The exact settings should be adapted to the project, but a Doxygen configuration using Graphviz might include options such as:
HAVE_DOT = YES
DOT_IMAGE_FORMAT = svg
INTERACTIVE_SVG = YES
CLASS_DIAGRAMS = YES
CLASS_GRAPH = YES
COLLABORATION_GRAPH = YES
GRAPHICAL_HIERARCHY = YES
INCLUDE_GRAPH = YES
INCLUDED_BY_GRAPH = YES
DIRECTORY_GRAPH = YES
CALL_GRAPH = YES
CALLER_GRAPH = YES
UML_LOOK = YES
TEMPLATE_RELATIONS = YES
DOT_GRAPH_MAX_NODES = 80
MAX_DOT_GRAPH_DEPTH = 5Enabling every available graph is not automatically the best choice.
Large systems can generate enormous diagrams that are technically accurate but practically unreadable. Call and caller graphs can also increase documentation-generation time substantially.
The objective is not to produce the maximum number of diagrams. It is to produce diagrams that answer useful questions.
Inheritance diagrams
Inheritance graphs show how classes derive from one another.
They can help engineers understand:
interface implementations;
abstract base classes;
specialisation hierarchies;
extension points;
and areas where inheritance has become unnecessarily deep.
A small hierarchy is usually easy to understand from the source. A large hierarchy spread across many files is not.
Visualising it may immediately reveal patterns that deserve attention:
a base class with too many responsibilities;
unrelated concepts sharing the same hierarchy;
derived classes depending on implementation details;
or an inheritance structure that would be clearer through composition.
The graph does not decide whether the design is good. It makes the design easier to discuss.
Collaboration diagrams
A collaboration graph shows the classes used by a particular class through members, parameters or related dependencies.
This is especially useful for identifying coupling.
A class that appears straightforward in isolation may depend on many other components. Those dependencies may be legitimate, or they may indicate that the class has accumulated too many responsibilities.
Collaboration diagrams can expose:
central classes with excessive dependencies;
domain objects coupled to infrastructure;
circular or bidirectional relationships;
unexpected dependencies across modules;
and objects that are difficult to test independently.
These observations can support code reviews and architectural discussions with concrete evidence.
Call and caller graphs
A call graph shows which functions are called by a particular function.
A caller graph works in the opposite direction, showing which functions call the selected function.
Together, they can help answer questions such as:
What happens after this method is invoked?
Which code paths depend on this function?
How widely would a change propagate?
Which execution path leads to a particular operation?
Is apparently unused code actually reachable?
Where should tests or instrumentation be added?
These graphs are particularly useful while investigating unfamiliar code or assessing the potential impact of a change.
However, they can become extremely large. Generating them globally for a substantial application may create more noise than insight.
Selective generation and sensible depth limits are often more effective.
Include dependencies
C++ header dependencies can have a significant effect on build times, coupling and maintainability.
Include graphs make these relationships visible.
They can help identify:
frequently included headers;
unnecessary transitive dependencies;
circular include structures;
implementation details exposed through public headers;
and headers that trigger large rebuilds.
A header used throughout the system may be an important shared abstraction—or a sign that unrelated concerns have become connected through a common dependency.
Visual evidence provides a useful starting point for investigation.
Accelerating onboarding
New engineers often receive a combination of repository access, a few high-level diagrams and verbal explanations from existing team members.
The missing element is usually the connection between the conceptual architecture and the actual code.
Generated documentation can help bridge that gap.
A new team member can begin with the class hierarchy or directory structure, navigate into important components and then follow relationships into the implementation.
This does not eliminate the need for mentoring or architectural documentation. It reduces the amount of basic structural discovery that must happen through trial and error.
It can also make onboarding less dependent on the availability and memory of individual engineers.
Revealing architectural blind spots
Architecture diagrams created manually often represent the system as it was intended to exist.
Generated diagrams represent relationships detected in the code.
The difference is important.
A manually maintained diagram may show clean separation between modules, while the generated dependency graph reveals that implementation details cross those boundaries repeatedly.
This does not mean generated documentation replaces deliberate architecture models. The two views serve different purposes:
the manual model describes the intended architecture;
the generated model provides evidence of the implemented structure.
Comparing them can reveal architectural drift.
Configuration quality determines usefulness
Generating documentation is easy. Generating useful documentation requires judgement.
A poor configuration may:
include irrelevant files;
omit important internal components;
generate graphs too large to read;
fail to resolve relationships correctly;
produce inconsistent links;
or spend significant time rendering diagrams that nobody uses.
A useful configuration should consider:
which directories belong to the product;
which generated or third-party files should be excluded;
whether internal symbols should be documented;
how templates and macros are processed;
which graph types provide real value;
appropriate limits for graph size and depth;
and how the output will be published and maintained.
This is where many quick guides stop too early. They show how to run Doxygen but not how to create a sustainable documentation system for a real codebase.
Documentation must remain current
Generated documentation can become part of the build or delivery process.
For example, a pipeline can regenerate it when relevant source files change and publish the result to an internal documentation location.
This provides several advantages:
documentation reflects the current code;
generation failures become visible;
broken references can be detected;
teams use a consistent configuration;
and manual documentation steps are reduced.
The generation process should remain proportionate. If complete call graphs make every build excessively slow, they may be better produced on a schedule or through a dedicated documentation pipeline.
Security and access considerations
Detailed technical documentation can expose information about internal architecture, class relationships and execution paths.
That information may be valuable to engineers—and equally useful to an attacker if published unintentionally.
Generated documentation should therefore have an explicit publication model:
public documentation for genuinely public interfaces;
restricted internal documentation for implementation details;
and appropriate controls around build artefacts and hosting.
Documentation generation should not silently turn private source structure into a publicly accessible website.
Tooling as part of engineering quality
Tools such as Doxygen and Graphviz do not improve architecture automatically.
They improve visibility.
That visibility helps engineers understand the software, discuss design decisions and identify relationships that may otherwise remain hidden.
This aligns closely with my return to hands-on engineering. Good engineering is not limited to writing code that works. It also involves making systems easier to understand, review, operate and evolve.
A maintainable system is one in which engineers can answer not only “What does this function do?” but also:
Why does this component exist?
What depends on it?
What will be affected if it changes?
Does the implementation still reflect the intended architecture?
Used carefully, generated visual documentation helps provide those answers.
The objective is not to replace reading code.
It is to make the right code—and the relationships around it—much easier to find.
This article is based on my original ideas, experience, analysis and conclusions. Artificial intelligence tools were subsequently used as editorial and research assistants to review grammar and wording, improve structure and presentation, organise some arguments into clearer logical sections, and help review references to legal, regulatory and technical concepts.
Where relevant, factual and regulatory references were checked against the sources cited in the article. AI assistance does not replace professional legal, regulatory, financial or technical advice, and the final selection, interpretation, opinions and conclusions presented here remain my own.
Comments
Post a Comment