en_US

Zooming All the Way In: Understanding C4 Code Diagrams – What They Are, When They Add Value, and Practical PlantUML Examples

What is a C4 Code Diagram?

The Code diagram is Level 4 — the deepest, most detailed level in Simon Brown’s C4 model.

The Ultimate Guide to C4 Model Visualization with Visual Paradigm's AI Tools - ArchiMetric

It shows:

  • Classesinterfacesenumsrecords, or other code-level constructs that implement a specific component (from Level 3).

  • Relationships between those classes (inheritance, composition, dependency, realization of interfaces, etc.).

  • Key design elements such as patterns applied inside the component (e.g., repositories, services, DTOs, domain entities, factories).

In practice, this level is almost always a UML class diagram (or a simplified variant) focused on one (or very few) components.

Important clarification:

  • Level 4 is not about the entire codebase.

  • It is not required to show every class.

  • It maps only the essential structure needed to understand how a complex or critical component is actually built.

  • The official C4 recommendation: ideally auto-generated from source code (via tools like Doxygen, Javadoc + UML plugins, yWorks, Structurizr, CodeSee, etc.) rather than hand-drawn.

When to Create a Code Diagram

Create Level 4 diagrams sparingly — only in these situations:

  • The component is highly complexmission-critical, or difficult to understand from source code alone (e.g., intricate domain logic, heavy use of design patterns, cryptographic flows, state machines, legacy code riddled with technical debt).

  • You are working in a highly regulated industry (finance, healthcare, aerospace, defense) where auditors or compliance teams demand explicit mapping from architecture → design → implementation.

  • During major refactoringstrangling a legacy component, or introducing a new architectural pattern (hexagonal, clean, vertical slice, DDD aggregates) — before/after views help communicate the change.

  • Onboarding senior developers or architects who need to quickly grasp non-obvious internal structure of a high-risk piece of code.

  • You have already invested in auto-generation tooling — so maintaining Level 4 costs almost nothing.

  • The team has agreed that “living documentation” at class level is valuable for this specific subsystem.

Do NOT create Level 4 diagrams when:

  • The component structure is obvious from good naming, small size, or clean code (most modern microservices fall here).

  • You already have good unit/integration testsclear interfaces, and explanatory comments.

  • Most of the team can navigate the code easily.

  • Maintenance cost outweighs benefit (hand-drawn class diagrams go stale very quickly).

Simon Brown and most practitioners emphasize: Most teams never need Level 4Levels 1 + 2 cover 80–90% of communication needs; Level 3 handles most of the rest. Level 4 is the exception, not the rule.

Why Use Code Diagrams? (When They Add Value)

  • Bridge architecture ↔ implementation — Shows how high-level components are actually realized in code.

  • Clarify complex internal design — Exposes use of patterns (Strategy, Factory, Decorator, Repository), layering violations, tight coupling, or clever domain modeling.

  • Support audits & compliance — Demonstrates that architectural decisions are followed through to code.

  • Aid refactoring & migration discussions — Before/after class structures make proposals tangible.

  • Reduce “tribal knowledge” — Helps new senior hires understand non-trivial parts faster than reading all source files.

  • Auto-generated versions become “living docs” — If tooling is in place, they stay accurate with almost zero effort.

How to Create a Great Code Diagram (Step-by-Step + Best Practices)

  1. Pick ONE component — Usually from a Level 3 diagram where internal complexity justifies the zoom.

  2. Decide: hand-drawn or generated?

    • Hand-drawn → only for workshops, proposals, or areas too messy for auto-tools.

    • Generated → preferred (PlantUML can still be used to style/tweak the output).

  3. Focus on essentials — Show:

    • Key classes/interfaces

    • Important relationships (→ dependency, — composition, <| realization, ^ inheritance)

    • Aggregates, entities, value objects (DDD style)

    • Critical patterns or anti-patterns you want to highlight

  4. Keep it small — 8–15 classes maximum. If larger → split into focused diagrams (e.g., “Authentication slice”, “Order processing entities”).

  5. Best Practices

    • Prefer auto-generation whenever possible (less staleness).

    • Use PlantUML classDiagram syntax — clean and versionable.

    • Add notes for non-obvious decisions (e.g., “Uses Anemic Domain Model – planned refactor”).

    • Avoid showing everything — omit trivial getters/setters, utility classes.

    • Store in repo → treat as code (commit .puml files near the component).

    • Use sparingly — one per complex component, not per microservice.

    • Combine with dynamic views (sequence/collaboration) if runtime flow is more important than static structure.

PlantUML Example – Authentication Component (Big Bank plc style extension)

Here is a realistic Level 4 example zooming into the Security / Authentication Component from the earlier API Application diagrams.

Zooming All the Way In: Understanding C4 Code Diagrams – What They Are, When They Add Value, and Practical PlantUML Examples

@startuml
title C4 Level 4 – Code Diagram: Authentication inside API Application

skinparam monochrome true
skinparam shadowing false
skinparam class {
  BackgroundColor White
  BorderColor Black
  ArrowColor Black
}

abstract class AuthenticationProvider {
  + authenticate(credentials): Authentication
}

class JwtAuthenticationProvider {
  - tokenProvider: JwtTokenProvider
  - userDetailsService: UserDetailsService
  + authenticate(credentials): Authentication
}

class JwtTokenProvider {
  - secretKey: String
  - validityInMilliseconds: long
  + generateToken(userDetails): String
  + validateToken(token): boolean
  + getUsernameFromToken(token): String
}

interface UserDetailsService {
  + loadUserByUsername(username): UserDetails
}

class DatabaseUserDetailsService {
  - userRepository: UserRepository
  + loadUserByUsername(username): UserDetails
}

class UserRepository {
  + findByUsername(username): Optional<User>
}

class User {
  - username: String
  - passwordHash: String
  - roles: Set<Role>
}

class JwtAuthenticationToken << (T,orchid) Authentication >> {
  - principal: UserDetails
  - credentials: Object
  - authorities: Collection<GrantedAuthority>
}

' Relationships
JwtAuthenticationProvider -up-> JwtTokenProvider : uses
JwtAuthenticationProvider -up-> UserDetailsService : uses
DatabaseUserDetailsService .up.|> UserDetailsService
DatabaseUserDetailsService --> UserRepository : uses
UserRepository --> User : returns

JwtAuthenticationToken .up.|> Authentication

note right of JwtAuthenticationProvider
  Primary authentication flow for JWT-based stateless sessions
end note

note bottom of JwtTokenProvider
  Signs & verifies JWTs using HS512
end note

@enduml

This small diagram:

  • Focuses only on authentication internals

  • Shows key classes, interfaces, and dependencies

  • Highlights patterns (provider, repository)

  • Uses notes for context

Paste into any PlantUML renderer — customize for your domain (e.g., replace JWT with OAuth2, add MFA classes, etc.).

Summary reminder: Level 4 is powerful but rare. Use it intentionally, prefer auto-generation, and never let it become busywork. Most value in C4 comes from Levels 1–3. Happy (selective) modeling!

Resource