Code Contour โ€” User Guide

See the shape
of your source code

Code Contour measures a codebase and shows you what it is made of. Point it at a folder and it reads every file, in 81 languages, and reports lines, comments, methods, cyclomatic and cognitive complexity, Halstead volume and maintainability, file by file and method by method. Charts turn those numbers into something you can act on, a click on any of them selects the files behind it, and the whole run exports as CSV, HTML, JSON or a printed PDF report.

๐ŸชŸ Windows 10 / 11 ๐Ÿ”ค 81 languages ๐Ÿ“ McCabe & Campbell ๐Ÿ•ธ๏ธ Kiviat, map, trend ๐Ÿ“ค CSV ยท HTML ยท JSON ยท PDF โŒจ๏ธ Command line
โ–ถ๏ธ
Measure a folder
Add folders, press F5, read the table
๐Ÿ•ธ๏ธ
Charts that select
Click a chart, get the files behind it
๐Ÿ”จ
Work between runs
What was written, rewritten and thrown away
โŒจ๏ธ
Command line
The same numbers, with an exit code for your build
Overview

Numbers you can act on

Code Contour is a metrics tool for people who have to look after a codebase they did not write all of. It measures what is there, says how sure it is, and never hides a file it could not read.

๐Ÿ“

Every number has a source

Complexity is McCabe, cognitive complexity is Campbell, the maintainability index is the Microsoft form. Nothing here is invented, and this guide says exactly how each number is arrived at, so you can check it against your own reading of the code.

๐Ÿ–ฑ๏ธ

Charts you can click

Every chart is drawn from the rows on screen, and a click on a bar, a spoke, a slice or a rectangle selects the files behind it. A picture nobody can act on is a screensaver.

๐Ÿงญ

Honest about itself

A file that could not be read is listed with the reason. A language it is not sure about carries a confidence. Code that is not yours, generated or vendored, is marked rather than quietly counted.

๐Ÿ”’

Private and offline

Your source never leaves your PC. No cloud, no telemetry, no account. The project file and every export are written where you put them.

โšก

Native and fast

A compiled Windows application with no runtime to install. It reads files that other programs are still writing, and it never blocks on a lock.

๐Ÿ•ฐ๏ธ

A history, not a snapshot

Keep an analysis in the project and it becomes a point you can come back to. A trend shows where the codebase went, and a work report says what was written between any two points.

๐Ÿ“œ

Where the idea came from. Code Contour was inspired by Source Monitor, a tool many developers remember and which is now frozen legacy. Aelyo Softworks is not affiliated with it in any way, has no relation to its authors or its owners, and none of its open source version was used or looked at in the making of this product. Code Contour is not a clone of it. It is a new application, written from nothing, with its own analysis engine, its own metrics, its own charts, its own reports, its own command line and its own file format, and with features that tool never had. What the two have in common is the belief that a codebase is worth measuring.

Getting Started

Installation

Code Contour is a single, self-contained Windows application. Once installed it appears in your Start menu and registers the .codecontour project type.

Get Code Contour

Code Contour is distributed through the Microsoft Store, so it stays up to date automatically and installs cleanly with no extra runtimes.

โฌ‡ Get it from the Microsoft Store
โœ…

Nothing to configure. Code Contour opens on an empty project. Add a folder, press F5, and you have your first analysis. Double-clicking a .codecontour file in Explorer opens that project.

Getting Started

The Window

One window holds everything. A panel down the left says what is being measured, the table in the middle is the result, and a summary along the bottom totals whatever the table is showing.

๐Ÿ“

Folders

The folders this project covers. Add, remove, or change one, and drop folders straight onto the window.

๐Ÿ’พ

Analyses

The runs this project has kept, most recent first, with the current run at the top when it has not been kept yet. Keep, remove, show and compare from here.

๐Ÿงพ

The table

One row per file. Sort by any column, right click a header to choose which ones show, right click a row for everything you can do to that file.

โž•

The aggregate line

Under the table, the totals of the rows on screen. It follows every filter, so it always describes what you are looking at rather than what was measured.

The Code Contour window with a codebase measured, the folders and analyses panels above the table
One window: the folders being measured, the analyses kept, the table, and the totals of whatever the table is showing.
๐Ÿ“ท

The pictures in this guide were taken of Microsoft's PowerToys repository, which is open source under the MIT licence. It is 5 737 files in 20 languages, which is a fair likeness of the kind of codebase this tool is for.

๐Ÿงฎ

The summary answers each column in its own way. A count is totalled, a complexity is averaged, an encoding says how many distinct ones there are, and the human language column says which languages were found and how sure it was.

Measuring

Projects & Folders

A project is a name, a .codecontour file, and the folders it covers, together with the rules it was measured under and the analyses it has kept.

๐Ÿ“‚ Adding folders

Use Add Folder... in the Folders panel, the same entry at the top of the Analysis menu, or drop a folder onto the window. A project can cover as many folders as you like, in as many languages as you like. Sub-folders are walked automatically.

The walk goes past the folders the system hides, a tool's cache, a recycle bin, a volume's own bookkeeping. That is a project setting and it is on by default. A folder you asked for by name is never judged that way, so a project whose root is hidden is scanned anyway.

๐Ÿ’พ Saving

File โ†’ Save Project writes the .codecontour document, which holds the folders, the rules, and every analysis kept. It is compressed JSON, so a project with a long history stays small. Recent projects are listed under File โ†’ Recent Projects, with entries to clear the list and to drop the projects whose file is gone.

โš ๏ธ

The counting rules belong to the project and are frozen once its first analysis is kept. A trend measured under two different sets of rules would move for a reason that is not the code, so Code Contour refuses to change them rather than let that happen.

Measuring

Running an Analysis

Analysis โ†’ Analyze, or F5, measures every file under every folder of the project. The status line says what it is doing, and you can stop it at any point.

โฑ๏ธ While it runs

A run is not one long wait, so the line says which of four things it is in, getting ready, reading what the repositories say about their own files, looking for files, and measuring them. Only the last can be counted against a total, and the walk names the folder it is in as it goes.

Stopping keeps what was measured so far and says so. Nothing is lost, and running it again is all it takes to have the whole thing.

๐Ÿ‘ฏ Second checkouts of the same code

A git worktree is a second checkout of a repository you already have, and they have become common: an AI agent is given one to work in, or you keep one to build the previous release while you edit the next. Under a folder being measured, one of those is the project's own files a second time, and counting them twice says the codebase doubled overnight and every file of it has a twin.

So a worktree found under a folder of the project is walked past, and the run says so: a clause on the status line, a line in the header of every report, and Analysis → Second checkouts passed over, which lists each one with the repository it is a checkout of, beside the list of files that could not be read. It is the one skip worth a sentence of its own, because bin and node_modules hold nothing of yours while this holds all of it again, and every folder a run passed over is listed under Analysis → Folders left out. The churn of two worktrees of one repository is read once rather than added to itself. A submodule is not a worktree and stays: it is code of its own that happens to live inside another repository. And a worktree you name as a project folder yourself is measured like any other folder, since asking for one on purpose is a different thing from meeting one on the way.

๐Ÿ”“ Files another program is holding

Every file is opened read only and shared as widely as a reader can share it, so a log or a database journal is read while its owner goes on writing it. Nothing waits for a lock and nothing retries, because a scan of a live tree would never end if it did.

A file whose owner shares nothing at all is counted out of the run, and never silently. The summary says how many there were, and Analysis โ†’ Files that could not be read lists them with the reason the system gave.

๐Ÿ”ก Encodings and line endings

Every file's encoding is detected as it is read, from its byte order mark where it has one and from its content where it has none, and the analysis reports which encoding each file turned out to be. A project says which encoding to assume for a file with no mark, and any encoding Windows knows can be chosen, 116 of them.

Line endings are reported the same way, and the summary says how many files disagree with the rest. A codebase where thirty files out of five thousand end their lines differently is a codebase with an editor setting somebody has not shared, and it is the kind of thing nobody notices until a diff is unreadable.

๐Ÿ’ก

The first analysis of a project also decides what the project thinks is normal. Later runs are measured against the same bands, which is what makes the Kiviat chart worth reading.

Measuring

Languages

Code Contour reads 81 languages, from C and C++ to Rust, Go, Swift, Kotlin, Zig, COBOL and Fortran, plus markup and data formats that a repository is full of and that no metrics tool should pretend are code.

๐Ÿท๏ธ Code, markup, data

Every language is one of three kinds, and the kind decides what is measured and what is merely counted:

โŒจ๏ธ

Code

Measured in full, methods, complexity, block depth, calls and maintainability. This is what the metrics are about.

๐Ÿ“ฐ

Markup

HTML, XML, CSS, Markdown. Lines and comments are real, control flow is not, so nothing pretends there is any.

๐Ÿ—ƒ๏ธ

Data

JSON, TOML, INI, Makefiles, Dockerfiles, protobuf. Counted, and JSON is genuinely measured, since it is half of what a modern repository holds.

Whether markup and data files are looked at is a project setting, so a project can be about its code alone.

๐Ÿ“‹ The whole list

Every language Code Contour reads. The dot says which kind it is: code in blue, markup in green, data in dark blue.

ABAP Ada Arduino Assembly AWK C CMake COBOL CoffeeScript C++ Crystal C# HLSL CUDA Cython D Dart Elixir Elm Erlang Fortran FoxPro F# GameMaker Language Go Groovy Haskell IDL Java JavaScript Julia Kotlin Lua MATLAB Nim Nix Objective-C OCaml Pascal Perl PHP PowerShell Prolog Python QML R Razor Ruby Rust SAS Scala Shell SQL Svelte Swift Tcl Terraform TypeScript Vala Visual Basic Verilog VHDL Vue X++ Zig CSS HTML Markdown XML ANTLR Batch Dockerfile GraphQL INI JSON Makefile Module definition Protocol Buffers Thrift TOML YAML

๐Ÿ”ฌ How each one is measured

Most languages are measured routine by routine. Code Contour finds the methods, functions or procedures, gives each its own complexity, statements, block depth and calls, and rolls them up to the file. That is the grain the metrics were designed for.

A few languages have no routine a lexer can honestly find, and those are measured as the whole they are written as, one set of numbers for the file rather than one per routine. COBOL is the clearest case: a paragraph is a name followed by a full stop and cannot be told apart from a sentence, and a section of the data division is not a routine at all. Prolog is the same, a clause is not a procedure. Nothing is invented for them, and their files simply carry no method count.

Only real code is measured. A file that is code first and markup second, a .razor, a .vue, a .svelte, is code here, since that is what somebody writing it is doing. A file that is markup with code sprinkled in is markup.

๐Ÿ—ƒ๏ธ

What JSON's numbers mean. JSON is measured rather than merely counted, since it is half of what a modern repository holds, but it has no control flow and nothing here pretends otherwise. A member is a statement. An object or array is a block, so depth is the nesting a reader has to follow. Complexity is that deepest nesting rather than a count of paths, because there are no paths. Anything whose extension ends in json is treated as JSON, since the format spawns dialects faster than any table can name them.

๐Ÿ“„ Extensions

A file's extension decides its language, and you can override any of it. File โ†’ File Extensions lists every extension Code Contour has an opinion about, what was decided for it, and lets you say otherwise, including saying that files of that kind hold no code at all. The mapping belongs to you rather than to one project, so it follows you from project to project.

An extension met for the first time is reported rather than guessed at, so a codebase full of an extension nobody has taught it about does not silently read as nothing.

๐ŸŒ The language it is written in

Separately from the programming language, Code Contour can tell you what human language the comments and strings are written in, with a confidence for each answer. Windows detects it, so it costs no dependency and no network. On a codebase worked on by several teams this is often the most surprising column in the table.

Measuring

Metrics

Every number has a source, and none of them is this tool's invention.

LinesEvery line of the file, blank ones included.
Code linesLines that carry code. Blank lines and comment lines can each be counted out, per project.
CommentsComment lines, with documentation comments countable separately.
StatementsWhat the language ends with a separator, per method and per file.
MethodsFunctions, methods, procedures, whatever the language calls them.
Cyclomatic complexityMcCabe. One, plus one for each if, for, foreach, while, case, catch, &&, || and ?. An else and a default add nothing, since they open no path that was not already counted.
Cognitive complexityCampbell. A whole switch costs one however many labels it has, an else costs one and nests nothing, a run of the same boolean operator costs one, and anything nested costs one more per level. It measures how hard code is to follow rather than how many paths it has.
Block depthHow deeply the code nests, per method and at its worst in the file.
CallsHow many calls a method makes.
Halstead volumeN logโ‚‚ n over the token stream.
Maintainability indexThe Microsoft form, computed per method, which is the grain it was designed for, and averaged over a file by lines. A file with no method at all falls back to its own tokens.
Percent branchesDecision points over statements.
Percent commentsComment lines over all lines.

๐Ÿ”€ Cyclomatic complexity, in detail

McCabe's measure counts the paths through a routine. Start at 1, the one path taken when nothing branches, then add 1 for every point where the code can go two ways:

A condition
if, elif, a ternary ?
A loop
for, foreach, while, do
A label of a switch
case
A handler
catch
A short circuit operator
&&, ||

An else and a default add nothing. They open no path that the if or the switch above them had not already opened, so counting them would count the same fork twice. A routine of 1 has one way through it. A routine of 30 has thirty, which is also, roughly, the number of tests it takes to cover.

Counting every case separately is the default. Modified complexity, a project option, counts a whole switch as one instead, on the argument that a reader takes in a table of ten labels more easily than ten separate ifs. Both are defensible, and a project settles on one before its first analysis so that its history is comparable.

๐Ÿง  Cognitive complexity, in detail

Campbell's measure asks a different question: not how many paths there are, but how hard the code is to follow. It is deliberately not the same number, and where the two disagree the disagreement is the point.

A whole switch
1, however many labels it has
An else
1, and it nests nothing
A run of one boolean operator
1 for the run, not 1 per operator
Anything nested inside something
1 more per level of nesting

So a && b && c costs 1, because it is one idea, while a && b || c costs 2, because the reader has to change gear in the middle. A switch over ten cases costs 1 where cyclomatic complexity charges 10, and a loop inside a condition inside a loop costs 1 + 2 + 3 where cyclomatic complexity charges 1 + 1 + 1.

Code Contour measures what a lexer can honestly see, and claims nothing more. Recursion and jumps to labels, which the original measure also charges for, add nothing here, because knowing that a call is recursive means resolving names across files and that is not what this tool does.

The language table is what tells the two measures apart, which is why a language whose case is written the other way round, Ruby's case being the head and its when the label, is still counted correctly.

๐Ÿ“š Where these come from

Every measure here is somebody's published work, not this tool's idea of one:

Cyclomatic complexity Thomas J. McCabe, A Complexity Measure, IEEE Transactions on Software Engineering, vol. SE-2, no. 4, December 1976, pages 308 to 320. doi:10.1109/TSE.1976.233837
Cognitive complexity G. Ann Campbell, Cognitive Complexity: a new way of measuring understandability, SonarSource white paper. sonarsource.com
Halstead volume Maurice H. Halstead, Elements of Software Science, Elsevier North-Holland, 1977.
Maintainability index Paul Oman and Jack Hagemeister, 1992, in the form Microsoft uses in Visual Studio. learn.microsoft.com

The maintainability index is computed exactly as that form states, per method, then averaged over a file by lines:

MI = max(0, (171 − 5.2 × ln(V) − 0.23 × CC − 16.2 × ln(LOC)) × 100 / 171)

where V is the Halstead volume, CC the cyclomatic complexity and LOC the lines of code. It lands between 0 and 100, and higher is better. A method is the grain that formula was designed for, which is why a file's figure is an average of its methods rather than the formula applied to the whole file. A file with no method at all falls back to its own tokens.

โš ๏ธ

Where we knowingly differ. Percent branches here is decision points over statements. Some tools count branch statements instead, with else, break and return in the list. The two land in the same range on real code, but they are not the same number, and a figure compared across tools should be read with that in mind.

๐ŸŽš๏ธ Rules you can change

A project decides how it counts, in Analysis โ†’ Project Settings, and the choices are settled before its first analysis and frozen with it:

Modified complexity
a whole switch counts as one
Ignore blank lines
they leave the code count
Ignore comment lines
they leave the code count
Ignore doc comments
documentation comments counted apart
Markup and data
whether those files are looked at

Two more sit in the same dialog and change nothing that was measured: whether files outside the project are left out of the analysis, and whether reports carry what the repository was written. Both can be changed at any time.

Every analysis kept writes its rules down, and a report names them whenever they are not the defaults.

๐Ÿ“Š

What counts as normal. Settings โ†’ What counts as normal holds the band each metric is compared against. That is what the Kiviat chart draws, and what makes a file "outside the band" in a report. The defaults are sensible, and a codebase with its own standards can move them.

The Table

Columns

The table shows one row per file. Choose the visible columns from Analysis โ†’ Columns or by right clicking the headers, and sort by any of them.

FileThe path, shortened in the middle when it has to give, so the name is never the part that goes.
LanguageThe programming language, with a certainty when it was not simply the extension.
Human languageWhat the comments and strings are written in, with a confidence.
Lines, Code, CommentsThe counts.
Methods, ComplexityHow many, and how complex the worst of them is.
Outside of projectWhether this file is your work, see Setting Files Aside.
Encoding, Line endingsHow the file is written, and whether it agrees with its neighbours.
Size, Last modifiedWhat the file system says.
๐Ÿ’ก

The aggregate line under the table follows the columns you show and the rows you have narrowed to, so it always totals what you are actually looking at.

โ†”๏ธ Widths

Every column can be made wider or narrower, the file name included. A line runs down each column boundary, through the heading and the totals under it, and dragging that line moves the edge. The widths are yours and are remembered, so the table opens the way you left it, and only the columns you actually moved are stored: one you never touched still follows whatever a later version thinks is sensible.

Drag a boundary
sets that column's width
Double click a boundary
fits that column to what is in it
Ctrl and double click
fits every column at once

The same three are on the menu the headers offer when you right click them, under the list of columns to show, together with Reset the column widths, which puts every column back to the width it ships with. Fitting measures the heading, the total and the rows on screen, so it fits what you can see rather than laying out every file in the project to ask.

๐Ÿ“

The file name column keeps whatever room the others leave, and never gives up more than a readable minimum. Turn on enough columns and the table becomes wider than the window, at which point a scroll bar appears under it and the headings, the totals and the rows all slide together. Shift with the wheel scrolls sideways as well. The list keeps its own scroll bar against the right edge of the window, where it always was.

The Table

Filters & Selection

Narrowing the table narrows everything. Every chart draws the rows on screen, the summary totals them, and an export writes them.

The button reads Everything until you narrow something, and it means everything: every language, the code that is not your own work, and the files you set aside. Narrow it and the button says what is left, in the same words every chart and export uses to say what it covers.

๐Ÿ”ค By language

Show one language, several, all, or none. A codebase of half a million lines of generated data becomes readable the moment the data leaves the table.

๐Ÿ–ฑ๏ธ By chart

Every chart selects. Click a spoke of the Kiviat and you get the files outside its band. Click a bar of the histogram and you get the files holding a method in that range. Sweep a rectangle over the scatter and you get the files inside it. Click a slice of the languages pie and you get that language.

Answering a chart replaces the previous answer rather than narrowing what was already narrow, so you can never paint yourself into a corner. A link above the table takes the narrowing back and shows everything again.

๐Ÿ”—

The selection is one thing shared by the table, the summary, the charts and the exports. Whatever you narrowed to is what an export writes, and a report says in words which rows it covers, so a file exported a month ago can still be trusted to say what it is.

The Table

Setting Files Aside

Not everything under a folder is your work, and counting it all is how a codebase comes to believe it has a million lines. Code Contour separates the two in three ways.

๐Ÿท๏ธ Outside of the project

Code that is not the project's own work is detected and marked in its own column: third party code sitting in the tree, generated files, and data scripts. Your own rule wins over the detection, so a folder you say is yours is yours. A row menu says what a file is, inside, outside, or whatever the analysis finds, and the answer is remembered for the next run.

Marked files stay in the table and stay countable. They are labelled rather than hidden, so the totals are honest and you can filter them out when you want to see only what your team wrote.

A project can go further and leave them out of the analysis altogether, Leave files outside the project out of the analysis in Analysis → Project Settings, changeable at any time since it changes nothing that was already measured. If that is how you always work, Settings → Projects has New projects leave outside code out of the analysis, and every project you make from then on starts that way. It is off to begin with: a file counted and labelled can still be filtered out of a chart, and one never measured cannot be brought back without measuring again.

๐Ÿ“ Folders never measured

A folder named bin, obj, node_modules, packages, vendor or one of two dozen others is never walked into, wherever it sits. Analysis → Excluded folder names lists every one of them: turn one off to measure it, a project whose source lives in out for instance, or add your own, Generated or ThirdParty. They are names rather than paths, so one entry covers every project of a solution.

And after a run, Analysis → Folders left out says exactly which folders were passed over and why, a name, a folder Windows hides, or a second checkout. It is the answer to "where are my files", and it means no tree ever leaves an analysis without a word. The folders you set aside are in that list too, under a reason of their own, since that is where you would look for them, but they are not skipped: their files are measured and marked, which is why they are still in the table.

๐Ÿšซ Exclusions

Right click a row to exclude a file, a whole folder, or every file of that extension. One click sets a branch aside and the next click gives it back. Clear all exclusions drops them all at once, after a confirmation.

๐ŸŒฟ What the repository says about itself

Rather than guess from a file name or a banner, Code Contour reads the rules the repository already keeps, its .gitignore and its .gitattributes. Those files are maintained by the people working there, which makes them worth more than anything a tool can infer:

A file git was told not to track
outside the project, reason Ignored
linguist-generated, linguist-documentation
generated
linguist-vendored
third party

Git's own glob is implemented rather than approximated, so a rule behaves here exactly as it behaves for git: * stops at a slash and ** does not, a rule holding a slash is measured from the folder it was written in, a trailing slash means a folder and everything under it, and ! takes back what an earlier rule said, the last matching rule deciding. The files at and under the scanned folder are read, and the ones above it up to the repository root.

It is on by default and settled before the first analysis. Once an analysis has been kept the choice is frozen, because the analyses already kept were measured under those rules and measuring the next one differently would compare two different projects.

The Table

The Source Viewer

Double click a row, or use View in its menu, to read the file itself. It is a read-only viewer with syntax colouring, and it is where a number stops being a number.

No IDE is needed to use Code Contour, and none has to be installed to read what it measured. The viewer knows the language from the analysis, so a file is coloured the way its language should be. It opens at the line you came from when you arrived from a chart or a report link, and it moves to the next or previous row of the table without going back to it. Esc closes it.

It also keeps your place. The line you left a file at is remembered with the project, so opening it again puts you back where you were rather than at the top, which matters on the kind of file that made you open the viewer in the first place. Turn it off in Settings under Remember where you were reading in each file.

The read-only source viewer showing a C++ file with syntax colouring and a minimap
Read-only, coloured for the language the analysis found, and opened at the line you came from.
๐Ÿ”’

Nothing in Code Contour edits your source. The viewer opens files read only and shares them with whatever else has them open, so reading a file here never gets in the way of your editor.

Charts

Kiviat

Charts โ†’ Kiviat chart draws the rows on screen against what this project considers normal. It is the fastest way to see the shape of a codebase, and the one picture worth opening first.

Each spoke is a metric, and the band around the middle is the range that metric usually falls in. A shape that sits inside the band everywhere is ordinary code. A spoke that pushes out is where this codebase is unusual, and clicking that spoke selects the files that put it there, worst first.

A row menu also draws the Kiviat of a single file, which is the same picture asked about one thing instead of a set.

The Kiviat chart of the PowerToys codebase, with four of nine metrics outside the band
Nine metrics against what this project calls normal. The line under the chart names the ones that fall outside it.
๐Ÿ’ก

The bands come from Settings โ†’ What counts as normal. If your codebase has standards of its own, move them there and the chart starts answering your question rather than a generic one.

Charts

Map of the Code

Charts โ†’ Map of the code draws every file as a rectangle, sized by its lines and nested inside its folders, so a whole codebase fits on one screen with its shape intact.

๐ŸŽจ Ask it a question

The colour of the map answers one question at a time, and you choose which:

Language
what this codebase is written in
Complexity
where the hard code is
Maintainability
where it will hurt to work
Change
what moved since the last analysis
Outside
how much of this is your work

๐Ÿ–ฑ๏ธ Moving around

A click on a folder opens it and the map redraws inside it. Backspace goes back up, Esc closes the map. A click on a file reads it over the map rather than instead of it, so you can look at the code and keep your place.

The map follows the table like every other chart, so filtering to one language maps that language alone.

The map of the code, one rectangle per file, nested by folder and coloured by language
Five thousand files on one screen. Area is code lines, colour is the language, and the folders keep their shape.
Charts

Complexity Charts

Two charts ask about complexity, one about how it is spread and one about where it comes from.

๐Ÿ“Š Complexity histogram

Charts โ†’ Complexity histogram counts the methods on screen across ranges of complexity. A healthy codebase is a tall bar on the left and a thin tail. A click on a range selects the files holding a method in it, worst first, which turns "we have complex code somewhere" into a list of files.

A histogram of method complexity, with twenty thousand methods in the lowest range and a hundred and fifty above twenty six
The shape to want. The last bar is the day's work, and clicking it says which files it is in.

โšซ Files by complexity

Charts โ†’ Files by complexity puts every file on screen as a dot, statements across and complexity up. The cloud shows the relationship between size and difficulty, and the dots that sit away from it are the files worth looking at. Sweep a rectangle over any part of the chart to select the files inside it.

A scatter chart of every file, statements across and complexity up
Size against difficulty. The dots off the cloud are the ones to open.
๐Ÿ–ฑ๏ธ

Because a chart can be swept, a click is decided when the pointer comes back up. A press that turned into a rectangle was never a click on whatever happened to be under it.

Charts

Language Charts

Two charts say what a codebase is made of, one about the languages it is programmed in and one about the language its people write in.

๐Ÿฅง Languages

Charts โ†’ Languages chart draws the languages of the rows on screen, as bars and as a pie, by files and by lines. The two often disagree, and the disagreement is the interesting part. Clicking a language selects its files.

The languages of a codebase as bars and as a pie, by code lines
By files or by code lines, whichever question you are asking.

๐ŸŒ Human languages

Charts โ†’ Human languages draws what the comments and strings are written in, with how sure each answer was. Only the project's own files are counted, since what a generator or a vendor writes its comments in says nothing about the people working here.

The human languages chart, mostly English with a tail of others and a count of files recognized as nothing
The chart says how sure it is, names every answer with its confidence, and counts the files it could make nothing of rather than hiding them.
History

Analyses

A run is a throwaway measurement until you keep it. Keeping it gives it a name and writes it into the project, and it becomes a point you can come back to, compare against, and report from.

๐Ÿ’พ Keeping a run

The Analyses panel lists what the project has kept, most recent first, with the current run at the top while it is still unkept. Keepโ€ฆ names it and adds it. Closing a project with an unkept run asks whether to keep it or drop it, so nothing is lost by accident.

A name is used once, because a comparison later asks for it by name. Remove drops a kept analysis, or discards the current run when it was never kept. Removing cannot be undone, and what was measured then cannot be measured again.

๐Ÿ‘๏ธ Looking at an old run

Picking a line of the panel shows that analysis in the table, with its own numbers, its own charts and its own exports, without measuring anything again. The whole window becomes that day's codebase.

The folders and analyses panels, with five analyses kept from four years of a codebase
Four years of one codebase, most recent first. Picking a line shows that day's analysis in the table.
๐Ÿ“ฆ

An analysis is self-sufficient. It carries the files it measured, the rules it was measured under and the folders it covered, so a project opened years later still explains itself.

History

Comparing & Trend

Two analyses make a comparison. Three or more make a trend.

๐Ÿ“ˆ Trend across analyses

Charts โ†’ Trend across analyses draws how one number moved across everything the project has kept, the current run included. Code lines, files, complexity per method, and the rest. The chart says how many analyses it covers and the dates it spans, so a rise is never read as a week when it was a year. The entry stays off until there are two points to draw a line through, the run in hand counting as one of them, and Analysis → Files that could not be read is off in the same way while the last run read everything.

A trend chart of code lines across five analyses spanning four years
Code lines across four years of PowerToys. The line under the chart says how far it moved and over which two analyses.

โš–๏ธ Comparing two

Compareโ€ฆ in the Analyses panel takes the analysis you picked and asks what to compare it against. The result says what moved and which way, in the same words the report uses.

The compare analyses dialog, offering an analysis to compare the current one against
Pick a line in the panel, press Compare, and say what to measure it against.
๐Ÿ’ก

A trend is only as honest as its rules. Because a project freezes its counting rules with its first kept analysis, every point on the line was measured the same way, so a move on the chart is always a move in the code.

History

Churn from Git

A trend needs analyses you kept. Your repository has been keeping its own account since the first commit, and Code Contour can read it.

A folder inside a git checkout shows a small history mark in the Folders list. When at least one of them does, Charts → Churn from git history opens the chart of what was written into it: lines added above the line, lines removed below it, one column per month.

The churn chart, lines added and removed per month across three years of PowerToys
Three years of PowerToys, 1 674 commits, read straight out of the checkout. The sentence underneath names the busiest month.
By monthOne column per month between the first commit read and the last, quiet months included, so a year of work is never drawn as though it were busy throughout.
By authorThe twelve busiest people, largest first, with names resolved through the repository's .mailmap, since the same person often commits under several addresses.
How far backThe last 12 months, the last 3 years, or the whole history. A long history takes a few seconds to read, once, and is kept for as long as the window is open.
Several repositoriesA project whose folders live in more than one checkout adds them up, and each repository is read once however many folders point at it. Two worktrees of one repository are one history, not two.
RenamesA moved file is counted as a move, the way git counts it by default. Without that, one afternoon of tidying reads as a heroic week.
๐Ÿ”’

Nothing leaves your computer. The library Code Contour reads repositories with is built without any network transport at all, so it reads the checkout on your machine and cannot contact a server even if asked to.

๐Ÿ“ค In the reports

The chart is one window, and the same history goes into the reports when you ask for it. Analysis → Project Settings has Include what the repository was written in reports, offered whenever one of your folders is inside a checkout and explained where none of them is. It belongs to the project rather than to the application, so it travels with the .codecontour file and a build reading that project reports the same thing you do.

HTML and PDFA What was written section: the monthly chart, then the busiest people with what each added, removed and committed.
JSONA churn object carrying the repositories, the period, every month and the busiest authors. It is null when no folder is in a repository, which is not the same fact as a repository nobody wrote in.
CSVNothing. A sheet is one table of files, and churn is a table of something else.
How muchThe last two years, and the ten busiest people. A report is written while you wait, and a whole history takes far longer to read than a report should.
From a script--churn on analyze and show, or nothing at all when the project you pointed at already asks for it.
๐Ÿ’ก

Churn says what was written. It cannot say what was rewritten: only two measured analyses can tell a rewrite from growth, which is what the work report is for. The two answer different questions and are worth reading together.

History

The Work Report

A trend says where the codebase got to. The work report says what was done to get there, between any two analyses the project kept.

WrittenLines added that were not there before.
DeletedLines that went away.
RewrittenLines written over code that was already there. This is the bar worth looking at, since a codebase being fought with is mostly rework.
TouchedFiles that changed at all, and the ones that only moved.
Which way it wentWhether complexity and maintainability improved or got worse over the period, and in how many files.
Where the work wentThe files that absorbed the most of it, with the long tail counted rather than listed.

The period is named at the top, the two analyses and the days between them, because the same amount of work over a week and over a year describes two different codebases.

The report writes as a spreadsheet, a page, a document or a PDF, and the command line writes the same file with its work command.

๐Ÿ”‘

The detailed work report is part of Code Contour Premium. Without it, Changes since previous analysis gives a real answer in six lines, code lines, files, complexity, maintainability, and which way the methods went.

Output

Exports

Everything on screen leaves the application in the shape you need, and every export covers exactly the rows you narrowed to.

๐Ÿงพ

CSV

The table as it stands, columns and all, in the dialect Excel opens as columns on a double click. Ctrl+E.

๐ŸŒ

HTML report

The whole report as one self-contained page, charts included, drawn as vectors so it prints and zooms cleanly. Nothing is fetched from anywhere when it opens.

๐Ÿงฉ

JSON

The whole analysis as a document with a versioned schema, written field by field so it is a contract rather than an internal type. This is what a build reads.

๐Ÿ“„

PDF report

The printed report, see The Report on Paper.

Every report carries the same footer, the product and version that wrote it and the date it was written, so a page found later can always say where it came from. The HTML, PDF and JSON reports also carry what the repository was written when the project asks for it.

๐Ÿ”—

The HTML report's file names are links. Clicking one opens that file in Code Contour, at the line, through the codecontour: link the application registers. A report you send to somebody who has the tool is a report they can walk into.

Output

The Report on Paper

File โ†’ Export report as PDF writes the report as a real printed document, with no browser anywhere in it.

It says the same things the window says, in the same words, and leaves out what only a screen can carry. Every file of a run would be a hundred pages of paper and a scroll bar on a page, so what is printed is the summary, the charts, the rankings and what is worth looking at.

The pages are laid out the way a document should be. A heading is never left alone at the foot of a page, a table that does not fit is split and writes its headings again at the top of the next page, and a path in a cell is shortened in the middle so the file name always survives. Page size follows your region, A4 or Letter.

๐Ÿ“

The charts in the PDF are the same charts the windows draw. The arithmetic is written once, so a chart on screen and a chart in a report can never disagree about where a point sits.

Reference

Command Line

Code Contour carries a console application, so a build can measure what a developer measures, under the same rules, and stop when a limit is passed.

๐Ÿ“ค Getting it

File โ†’ Extract the command line tool writes the executable wherever you ask. It is one file, with no runtime beside it, which is what a build agent wants to unpack. The copy it writes names the person it was written for, so a copy that turns up elsewhere still says where it came from.

โŒจ๏ธ Commands

analyzeMeasures folders, or a project, and writes what was asked for.
showReads an analysis a project kept and writes it, without measuring anything again.
analysesLists the analyses a project kept, and removes one when asked.
workMeasures the work done between two analyses a project kept.
churnReads what was written into the repository a folder is in, by month or by author, without measuring anything.
--churnA switch rather than a command: analyze and show put the churn into the HTML, PDF and JSON reports they write.
languagesLists the languages this tool reads, by identifier.
extensionsLists every extension it has an opinion about, and what was decided for it.
infoSays what this is and what it is running on.
licenseSays who this copy was written for.

Each of the four operational commands writes any of the four shapes, --json, --csv, --html and --pdf, and --json - writes the document to standard output so a pipeline can read it. --project reads a project saved by the window, so a build measures what the developer measures. --keep writes the run into the project, which is how a build gets a history of its own.

๐Ÿšฆ Exit codes

The run was made
0
It could not be made
1
A limit was passed
2

--max-complexity and --min-maintainability are the limits, and a limit nobody gave is a limit nothing can fail, so a run with neither only reports. What failed is said on standard error, which leaves standard output to whatever was asked for.

โš™๏ธ

The extension mapping is looked for in three places, in the order you would expect: the file named by --settings, then a settings file sitting beside the executable, which is how a copy dropped on a build agent carries its own rules without touching that machine's, then the settings the application writes for the person logged in.

Reference

Settings

Settings belong to you and follow you between projects. Rules that decide how a number is counted belong to the project instead, so a history is never measured two ways.

Appearance & behaviorTheme, and how the window behaves.
File extensionsWhat each extension is, and which ones hold no code.
Human languagesWhich human languages you expect to meet in code. A file is shown as the first language of its list that you expect, and one whose whole list is unexpected is flagged for you to look at.
What counts as normalThe band each metric is compared against, which is what the Kiviat draws.
Remember readingWhether the place you left a file at is kept, so it opens there again.
ProjectsRecent projects, and what a new project starts with, including whether it leaves code that is not your team's own work out of the analysis. That one is off to begin with, since measuring it and marking it in its own column is what keeps the totals honest, and each project can decide for itself in Analysis → Project Settings.
StorageWhere the settings live, with a way to open the folder.

Any file can be read in any encoding Windows knows. A project says which encoding a file with no byte order mark is read in, and the list puts the ones a codebase actually meets first.

Reference

Free & Premium

Code Contour is free, and a one-time purchase from the Microsoft Store lifts its limits. Everything you measure is yours either way, and nothing you have already kept is ever taken away.

๐Ÿ†“ Free

The whole of the analysis, every language, every metric, every chart, the source viewer, the CSV, HTML, JSON and PDF reports of the current analysis, and a project that keeps three analyses, a baseline, the previous one and the current one, which is enough to see a trend. The trend draws the last three points, and the comparison compares the current analysis with the one before it.

๐Ÿ”‘ Premium

๐Ÿ•ฐ๏ธ

The complete history

Keep as many analyses as you like, and draw the trend across all of them.

โš–๏ธ

Any two analyses

Compare any point of the history against any other, not only the last two.

๐Ÿ”จ

The work report

What was written, deleted and rewritten between two analyses, and where the work went.

๐ŸŒฑ

Churn from git

What was written into your repository, month by month and by whom, read on your own machine.

โŒจ๏ธ

The command line

Write the console application out and measure from a script, a hook or a build.

๐Ÿ”’

Premium is a durable add-on, bought once. A project made by a premium user opens whole for anybody, every analysis in it can be shown and reported, and only keeping another one is refused. Nothing you have measured is ever hidden or removed.

Reference

Keyboard Shortcuts

Project

New project
Ctrl+N
Open projectโ€ฆ
Ctrl+O
Save project
Ctrl+S
Export rows as CSVโ€ฆ
Ctrl+E

Analysis

Analyze
F5
Open the file under the pointer
double click
Add a row to the selection
Ctrl+click

Charts & viewer

Close a chart or the viewer
Esc
Back up a level in the map
Backspace
Select a range on the scatter
drag a rectangle
Reference

Tips & Tricks

๐ŸŽฏ Start with the map, not the table

Open Map of the code and colour it by Outside. Before any metric means anything, you want to know how much of what you just measured your team actually wrote.

๐Ÿ“‰ Turn a worry into a list

"We have some complex code" is not actionable. Open the Complexity histogram, click the worst range, and the table becomes the list of files to look at, worst first.

๐Ÿ—“๏ธ Keep a baseline on day one

Keep an analysis before you start work on a codebase, and name it for the date. Everything you do afterwards is measurable against it, and the work report can tell you what a month of effort actually changed.

๐Ÿงน Exclude once, not every time

Exclusions and the inside or outside answer you give a file are remembered with the project, so a tree full of vendored code only has to be sorted out once.

๐Ÿค– Put it in the build

Write the command line tool out, drop it on the agent with a settings file beside it, and run analyze --project with --max-complexity. The build fails with exit code 2 the day something crosses the line, and --keep gives the build a history of its own.

๐Ÿ“จ Send the HTML, not a screenshot

The HTML report is one self-contained file with the charts inside it, and the file names in it are links back into Code Contour. It is the fastest way to hand a codebase review to somebody else.

Enjoying Code Contour?

A quick rating on the Microsoft Store is the best way to say thanks. It helps other developers find Code Contour, and it takes less than a minute.

โ˜… Rate Code Contour on the Microsoft Store
Code Contour โ€” User Guide v1.0, Copyright (C) 2025-2026 Aelyo Softworks. All rights reserved.