Quick summary
Setup
API
Changelog
Introduction
Ideas
Usage
Possibilities
Critique
Alternatives
Examples
Glossary
Contact
A version number is merely a label; it conveys no other meaning.
The changelog contains any and all information concerning switching from one version to another.
[com.sagevisuals/chlog "0"]
com.sagevisuals/chlog {:mvn/version "0"}
(require '[chlog.core :refer [generate-all-changelogs]])
Q: What will happen if we switch from this version to that version?
A: Consult the changelog, not the version number.
We ask too much of our software version numbers, and we should expect more from our changelogs. A major.minor.patch
sequence of numbers simply doesn't have enough bandwidth to tell us how to make good decisions about switching versions. On the other hand, html/markdown changelogs could potentially convey that information, but require a person to read and interpret it.
Ideally, a changelog would contain a compact representation of what changed from one version to the next, and operational implications of those changes. If we use functions foo
and bar
, but only function baz
changed, it's safe to switch. Or, function baz
changed and we use it, but the change is non-breaking, so, again, it's safe to switch. Or, maybe baz
was changed in a way that does break the way we use it, but the changelog tells us why it changed and how to manage the switch.
Changelogs are often served as html or markdown files, intended for people to read. But to convey the depth of information we're talking about requires a high-level of discipline by the authors to comprehensively write all that out. Mistakes and omissions are bound to happen with free-form text, not to mention irregularities that thwart parsing, and in the end, a person has to synthesize a lot of bits to make a decision. html/markdown is ill-suited for this purpose.
Storing a changelog in Clojure data structures offers many potential benefits. The data can be written, stored, retrieved, and manipulated programmatically. It can be validated for correctness and completeness. And we could write utilities that answer detailed questions about what it would be like to switch from one version to another.
Chlog is a library that tests these ideas. It encompasses several parts. First, it promotes a set of ideas, some of which we've already mentioned. Second, Chlog proposes a set of specifications for such a changelog. Third, Chlog offers an experimental implementation that maintains an .edn
changelog specification, validates it, and generates easily-readable html and markdown webpages based upon the changelog data.
The resulting changelog looks like this.
Versioning software with major.minor.patch
numbers attempt to convey the notion Yes, we can safely upgrade to such-and-such version. But the granularity is poor. What if a dependency does have a breaking change, but the breaking change is in a portion of the dependency that we don't use. Version numbers ought to merely be a label to differentiate one release from another.
If a version number is merely a label without semantics, how would someone judge whether to switch from one version to another? A detailed, concise, regularly-formatted changelog conveys all the information necessary to make an informed decision about if there is any benefit to change versions, if changing version will require changes to on the consuming side, and if so, what changes are necessary.
A later version is not promised to be better, merely different. The changelog authors will provide dispassionate information about the changes, and the people using the software can decide whether it is worth switching.
Chlog is an experiment to detangle version numbers from changelog information. A version number n
makes no claim other than it was released some time later than version n-1
.
The changelog.edn
is the single, canonical source of information. All other representations (html/markdown, etc) are derived from that and are merely conveniences.
A human- and machine-readable changelog.edn
will accompany each version. changelog.edn
is a tail-appended file constructed from all previous releases, possibly automatically-composed of per-version changelog-vN.edn
files in a sub-directory.
The Chlog experiment focuses on the changelog being the sole source of information on what will happen when switching versions. For that to succeed, the entries must accurately communicate whether a change is breaking. Not every change can be objectively categorized as either breaking or non-breaking (more on that in a moment). To have empathy for other people is tricky. If all changes are claimed as breaking, the concept loses its meaning and purpose. But if a supposedly safe change ends up breaking for someone else, trust is lost.
Within a changelog, seeing :breaking? false
indicates that switching to that version will work as it worked before with zero other changes (including changes in dependencies). Otherwise, the change is a breaking change, indicated by :breaking? true
.
As a rough starting guideline, the following kinds of changes are probably breaking.
Likewise, the following kinds of changes are probably non-breaking.
These are just starting guidelines. Careful judgment may say that a change in a function's defaults will in all cases be a non-breaking change. Or, a change in the documentation might be so severe that it's elevated to a breaking change.
One important kind of change that kinda defies categorization is bug-fixes. According to the notion that a non-breaking change must be a perfect drop-in replacement, a bug fix would classify as a breaking change. Tentative policy: Bug fixes are non-breaking changes, but it depends on the scenario.
Each version has required information that is explicitly delineated in the specifications. Correctness of a changelog, or any sub-component of the changelog, may be verified by validating the changelog against those specifications.
A changelog is mutable. Corrections are encouraged and additions are okay. The changelog itself is versioned-controlled data, and the html/markdown documents that are generated from the changelog data are also under version-control.
Yanked or retracted releases can simply be noted by revising the changelog data.
Much of the changelog data is objective (e.g., dates, email), but some is merely the changelog author's opinions. That's okay. The changelog author is communicating that opinion to the person considering switching versions. The changelog author may consider a particular bug-fix :high
urgency, but the person using the software may not.
There are four components to using Chlog.
Maintain changelog data in edn
files.
Declare and require the dependency.
Create an options file.
Generate the changelog documents.
Changelog information is stored as a series of nested collections in .edn
files. Every version is represented by the following map.
{:version ___
:date {:year ___
:month ___
:day ___ }
:responsible {:name ___
:email ___ }
:project-status ___
:breaking? ___
:urgency ___
:comment ___
:changes [...]}
This map (and all the following) is formally and canonically specified with a Speculoos style specification.
Briefly, the version parts are:
:low
, :medium
, or :high
.A changelog is a tail-appended vector of one or more such version hash-maps. Furthermore, a version hash-map may have zero or more change hash-maps associated to the :changes
key. A change hash-map looks like this.
{:description ___
:reference {:source ___
:url ___}
:change-type ___
:breaking? ___
:altered-functions []
:date {:year ___
:month ___
:day ___ }
:responsible {:name ___
:email ___ }}
Besides a sequence of :altered-functions
, a change may contain sequences of :added-functions
, :deprecated-functions
, :moved-functions
, :removed-functions
, and :renamed-functions
.
The parts of a change hash-map are:
The changelog data may be manipulated and queried with any suitable Clojure function, such as get-in
, assoc-in
, update-in
, etc. Chlog includes specifications for changelog data and utilities for performing those validations, but any validation facility, such as clojure.spec.alpha
, may be used.
The options file is an edn file (example) that contains a map which supplies required information for generating a changelog. It also declares preferences for other optional settings.
Required keys:
:project-name-formatted
Project name (string) to display on changelog html/markdown documents.
:copyright-holder
Name displayed in the copyright statement in the footer of the changelog.
:UUID
Version 4 Universally Unique Identifier. Suggestion: eval-and-replace (random-uuid)
. Default nil
.
Optional keys (defaults supplied by chlog_defaults.edn
):
:changelog-entries-directory
Alternative directory to find changelog .edn
files. Include trailing '/'. Defaults to resources/changelog_entries/
.
:changelog-data-file
Alternative .edn
file that contains the changelog data. May include load-file
-ing for easier organization and version control. Defaults to changelog.edn
.
:changelog-html-directory
Alternative output html directory (string). Include trailing '/'. Defaults to 'doc/'.
:changelog-html-filename
Alternative output html filename (string). Defaults to 'changelog.html'.
:changelog-markdown-directory
Alternative output markdown directory (string). Include trailing /
. Defaults to '' (i.e., project's root directory).
:changelog-markdown-filename
Alternative output markdown filename (string). Defaults to 'changelog.md'.
The Chlog library generates html and markdown changelog files from hiccup source. To generate the html and markdown files. We could evaluate…
(generate-all-changelogs (load-file "resources/chlog_options.edn"))
…in whatever namespace we loaded generate-all-changelogs
. Or, we could copy resources/chlog_generator.clj
and evaluate all forms in the namespace (cider command C-c C-k
).
Chlog produces two files. The first is a 'markdown' file that's actually plain old html, abusing the fact that html passes through the markdown converter. By default, this markdown file is written to the project's root directory where GitHub can find and display the changelog. We don't need a dedicated markdown converter to view this file; copy it to a GitHub gist and it'll display similarly to when we view it on GitHub. The second file, by default written to the resources/
directory, is a proper html document with a <head>
, etc., that is viewable in any browser. We may want to copy over the css file for some minimal styling.
When changelog information is stored as Clojure data, it opens many intriguing possibilities.
Changelog data could be used to generate formatted html or markdown webpages for casual reading. Chlog currently implements this.
A js/cljs
widget embedded in a webpage that presents a current version selector and a target version selector. Then, based on each selection, the utility would collapse all the intervening versions and list the breaking and non-breaking changes. Someone considering switching versions could quickly click around and compare the available versions. Switching from version 3 to version 4 introduces one breaking change whereas Switching from version 3 to version 5 involves that same breaking change, plus another breaking change in a function we don't use. Therefore, switching to version 4 or version 5 is equivalent.
A utility that could scan the codebase and list all the functions used from a particular dependency. If we were curious about switching versions of that dependency, that list of functions would be compared to the list of functions with breaking changes. If there was no intersection of the lists, it's safe to switch versions. If the codebase did use a function with a breaking change, the changelog would communicate what changed, and how involved it would be to switch versions.
On the one hand, making the changelog normalized data lends itself to straightforward analysis web display. On the other hand, is it a strategic mistake to require that every change be a member of an enumerated set? Is having a change-kind :other
a red flag? It's fairly typical for one of my commits be include an update to the source code, accompanied by some additional unit tests, and some kind of change in the documentation. At the moment, I would categorize it as a :altered-function
, with the implicit understanding that a person reading the changelog would care about that the most, and that the accompanying unit tests and docs are subordinate.
But the rigidity causes concern, and perhaps only extended use will reveal if it's a deal-breaker.
Classic markdown/html files Discussed above.
Changelog generated from version control commit log At first glance, a changelog generated from a version control commit log seems natural. It is certainly easy, and there are many concepts in common. However, the purpose of a changelog is different enough to merit its own focus.
Version control logs communicate information about the development history to developers of that software. Changelogs communicate the details of the released versions to people who consume the software. Related, but subtly different.
A commit message notes fine-grained changes to the software, somewhat like a diary of software development. It would feel a bit oppressive to have to consider how every commit message would appear in a public changelog. The evolution of software is noisy. Commit messages may involve false starts, mistakes, and dead ends. They are meant to be read by people developing the software itself. Plus, writing commit messages that also serve as a changelog entry would require some kind of standards or specifications, or heroic discipline by the authors. Finally, commit messages typically do not concern themselves about whether the change is breaking for the people using the software.
Changelogs, on the other hand, should clearly and concisely communicate, to people using the software, the differences between one version and another. It could be fine- or coarse-grained, but the freedom to decide should be independent of the version control commit log. Authoring a changelog requires care, judgment, and empathy for people ultimately using the software, and is a task somewhat different from wrangling version control commit messages.
Even at this early stage of its life, Chlog can alleviate most of that labor. Keep the changelog as .edn
data, and Chlog will take care of the html/markdown.
Changelog for a specification library.
Changelog for a collection manipulation library.
Changelog for a ReadMe generator library.
Changelog for a changelog generator library.
Changelog for a fictitious library.
A sequence of notable versions of project. Concretely, a tail-appended vector containing one or more version hash-maps. Typically resides in an .edn file where Chlog can locate it. The changelog contains information about the software that helps people understand what will happen when switching from one version of the software to another.
Within a version, a report of some facet of the software that is different. Concretely, a hash-map containing information about the change kind (e.g., added function, deprecated function, implementation change, performance improvement, bug-fix, etc.), person responsible, namespaced function symbols involved, and an issue reference (e.g., GitHub issue number, JIRA ticket, etc.).
A change that can be installed with zero other adjustments and the consuming software will work as before. Otherwise, the change is breaking.
A notable release of software, labeled by a version number. Concretely, a hash-map containing information about the release date, the person responsible, the urgency of switching, breakage, free-form description, and a listing of zero or more detailed changes.
This program and the accompanying materials are made available under the terms of the MIT License.
Can you improve this documentation?Edit on GitHub
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close