way_of_working 2.0.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -1
- data/CONTRIBUTING.md +62 -0
- data/README.md +186 -13
- data/lib/way_of_working/audit/github/auditor.rb +44 -0
- data/lib/way_of_working/audit/github/generators/exec.rb +163 -0
- data/lib/way_of_working/audit/github/rules/base.rb +91 -0
- data/lib/way_of_working/audit/github/rules/registry.rb +36 -0
- data/lib/way_of_working/audit/github/rules/unknown.rb +19 -0
- data/lib/way_of_working/audit/github.rb +25 -0
- data/lib/way_of_working/changelog/keepachangelog/generators/init.rb +118 -0
- data/lib/way_of_working/changelog/keepachangelog/github_audit_rule.rb +32 -0
- data/lib/way_of_working/changelog/keepachangelog/templates/docs/way_of_working/changelog.md +73 -0
- data/lib/way_of_working/changelog/keepachangelog.rb +35 -0
- data/lib/way_of_working/code_of_conduct/contributor_covenant/generators/init.rb +39 -0
- data/lib/way_of_working/code_of_conduct/contributor_covenant/github_audit_rule.rb +32 -0
- data/lib/way_of_working/code_of_conduct/contributor_covenant/templates/CODE_OF_CONDUCT.md.tt +133 -0
- data/lib/way_of_working/code_of_conduct/contributor_covenant/templates/docs/way_of_working/code-of-conduct.md +78 -0
- data/lib/way_of_working/code_of_conduct/contributor_covenant.rb +36 -0
- data/lib/way_of_working/decision_record/madr/generators/init.rb +41 -0
- data/lib/way_of_working/decision_record/madr/generators/new.rb +69 -0
- data/lib/way_of_working/decision_record/madr/github_audit_rule.rb +50 -0
- data/lib/way_of_working/decision_record/madr/templates/.github/ISSUE_TEMPLATE/decision-record.md +26 -0
- data/lib/way_of_working/decision_record/madr/templates/docs/decisions/0000-use-markdown-any-decision-records.md +30 -0
- data/lib/way_of_working/decision_record/madr/templates/docs/decisions/README.md +7 -0
- data/lib/way_of_working/decision_record/madr/templates/docs/decisions/adr-template.md.tt +82 -0
- data/lib/way_of_working/decision_record/madr/templates/docs/decisions/index.md +48 -0
- data/lib/way_of_working/decision_record/madr/templates/docs/way_of_working/decision-records.md +195 -0
- data/lib/way_of_working/decision_record/madr.rb +53 -0
- data/lib/way_of_working/git/repo_reader.rb +2 -2
- data/lib/way_of_working/inclusive_language/alex/generators/exec.rb +51 -0
- data/lib/way_of_working/inclusive_language/alex/generators/init.rb +34 -0
- data/lib/way_of_working/inclusive_language/alex/github_audit_rule.rb +35 -0
- data/lib/way_of_working/inclusive_language/alex/templates/.alexignore +2 -0
- data/lib/way_of_working/inclusive_language/alex/templates/.alexrc +3 -0
- data/lib/way_of_working/inclusive_language/alex/templates/.github/workflows/inclusive-language.yml +16 -0
- data/lib/way_of_working/inclusive_language/alex/templates/docs/way_of_working/inclusive-language.md +28 -0
- data/lib/way_of_working/inclusive_language/alex.rb +48 -0
- data/lib/way_of_working/pull_request_template/hdi/generators/init.rb +26 -0
- data/lib/way_of_working/pull_request_template/hdi/github_audit_rule.rb +32 -0
- data/lib/way_of_working/pull_request_template/hdi/templates/.github/pull_request_template.md +51 -0
- data/lib/way_of_working/pull_request_template/hdi/templates/docs/way_of_working/pull-request-template-and-guidelines.md +50 -0
- data/lib/way_of_working/pull_request_template/hdi.rb +35 -0
- data/lib/way_of_working/readme_badge/paths.rb +5 -2
- data/lib/way_of_working/version.rb +1 -1
- data/lib/way_of_working/versioning/semver/generators/init.rb +20 -0
- data/lib/way_of_working/versioning/semver/github_audit_rule.rb +49 -0
- data/lib/way_of_working/versioning/semver/templates/docs/way_of_working/versioning.md +71 -0
- data/lib/way_of_working/versioning/semver.rb +35 -0
- data/lib/way_of_working.rb +15 -0
- data/way_of_working.gemspec +3 -0
- metadata +88 -3
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'date'
|
|
4
|
+
|
|
5
|
+
module WayOfWorking
|
|
6
|
+
module DecisionRecord
|
|
7
|
+
module Madr
|
|
8
|
+
module Generators
|
|
9
|
+
# This generator add a new MADR decision record to the doc/decisions folder
|
|
10
|
+
class New < Thor::Group
|
|
11
|
+
argument :name, type: :string, required: true, desc: 'The title of the decision record'
|
|
12
|
+
|
|
13
|
+
include Thor::Actions
|
|
14
|
+
|
|
15
|
+
source_root WayOfWorking.root.join('lib', 'way_of_working', 'decision_record', 'madr', 'templates')
|
|
16
|
+
|
|
17
|
+
def create_decision_record_file
|
|
18
|
+
case behavior
|
|
19
|
+
when :invoke
|
|
20
|
+
invoke_decision_record_file
|
|
21
|
+
when :revoke
|
|
22
|
+
revoke_decision_record_file
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def invoke_decision_record_file
|
|
29
|
+
@decision_date = Date.today.strftime('%Y-%m-%d')
|
|
30
|
+
@index = next_decision_number.to_i
|
|
31
|
+
@title = name
|
|
32
|
+
|
|
33
|
+
# from https://raw.githubusercontent.com/adr/madr/3.0.0/template/adr-template.md
|
|
34
|
+
template 'docs/decisions/adr-template.md',
|
|
35
|
+
"docs/decisions/#{next_decision_number}-#{dashed_name}.md"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def revoke_decision_record_file
|
|
39
|
+
matching_files = Dir.glob("[0-9][0-9][0-9][0-9]-#{dashed_name}.md",
|
|
40
|
+
base: File.join(destination_root, 'docs/decisions'))
|
|
41
|
+
raise "No matching decision record for '#{dashed_name}'" if matching_files.empty?
|
|
42
|
+
|
|
43
|
+
# based on Thor's remove_file
|
|
44
|
+
path = File.expand_path("docs/decisions/#{matching_files.first}", destination_root)
|
|
45
|
+
|
|
46
|
+
say_status :remove, relative_to_original_destination_root(path)
|
|
47
|
+
return unless !options[:pretend] && (File.exist?(path) || File.symlink?(path))
|
|
48
|
+
|
|
49
|
+
require 'fileutils'
|
|
50
|
+
::FileUtils.rm_rf(path)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def next_decision_number
|
|
54
|
+
existing_decisions = Dir.glob('[0-9][0-9][0-9][0-9]-*.md',
|
|
55
|
+
base: File.join(destination_root, 'docs/decisions'))
|
|
56
|
+
last_number = existing_decisions.map do |filename|
|
|
57
|
+
filename.match(/\A(\d{4})-/)[1].to_i
|
|
58
|
+
end.max || -1
|
|
59
|
+
format('%04d', last_number + 1)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def dashed_name
|
|
63
|
+
name.downcase.gsub(/[\s_()]/, '-').gsub(/-+/, '-')
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'way_of_working/audit/github/rules/base'
|
|
4
|
+
|
|
5
|
+
module WayOfWorking
|
|
6
|
+
module DecisionRecord
|
|
7
|
+
# The namespace for plugin
|
|
8
|
+
module Madr
|
|
9
|
+
# This rule checks for the MADR Decision Record template.
|
|
10
|
+
class GithubAuditRule < ::WayOfWorking::Audit::Github::Rules::Base
|
|
11
|
+
ADR_TEMPLATE_PATH = 'docs/decisions/adr-template.md'
|
|
12
|
+
DECISION_ZERO_PATH = 'docs/decisions/0000-use-markdown-any-decision-records.md'
|
|
13
|
+
WOW_DOCUMENTATION_PATH = 'docs/way_of_working/decision-records.md'
|
|
14
|
+
|
|
15
|
+
source_root WayOfWorking.root.join('lib', 'way_of_working', 'decision_record', 'madr', 'templates')
|
|
16
|
+
|
|
17
|
+
def validate
|
|
18
|
+
@errors << 'No MADR Decision Record template found' unless repo_file_contains?(
|
|
19
|
+
ADR_TEMPLATE_PATH, '## Context and Problem Statement'
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
unless repo_file_contains_source_file?(WOW_DOCUMENTATION_PATH)
|
|
23
|
+
@errors << 'Stale or missing documentation for using MADR Decision Records'
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
return if repo_file_contains_source_file?(DECISION_ZERO_PATH)
|
|
27
|
+
|
|
28
|
+
@errors << 'No 0000-use-markdown-any-decision-records.md Decision Record found'
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def repo_file_contains_source_file?(path)
|
|
34
|
+
repo_file_contains?(path, File.read(self.class.source_root.join(path)))
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def repo_file_contains?(path, text)
|
|
38
|
+
remote_content = repo_file_contents(path)
|
|
39
|
+
return false if remote_content.nil?
|
|
40
|
+
|
|
41
|
+
remote_content.include?(text)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
::WayOfWorking::Audit::Github::Rules::Registry.register(
|
|
46
|
+
GithubAuditRule, 'MADR Decision Records'
|
|
47
|
+
)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
data/lib/way_of_working/decision_record/madr/templates/.github/ISSUE_TEMPLATE/decision-record.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Decision Record
|
|
3
|
+
about: Create a ticket to discuss or propose a new decision record
|
|
4
|
+
title: ''
|
|
5
|
+
labels: decision record
|
|
6
|
+
assignees: ''
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
## Context and Problem Statement
|
|
10
|
+
|
|
11
|
+
{Describe the context and problem statement, e.g., in free form using two to three sentences or in the form of an illustrative story.
|
|
12
|
+
You may want to articulate the problem in form of a question and add links to collaboration boards or issue management systems.}
|
|
13
|
+
|
|
14
|
+
<!-- This is an optional element. Feel free to remove. -->
|
|
15
|
+
## Decision Drivers
|
|
16
|
+
|
|
17
|
+
* {decision driver 1, e.g., a force, facing concern, …}
|
|
18
|
+
* {decision driver 2, e.g., a force, facing concern, …}
|
|
19
|
+
* … <!-- numbers of drivers can vary -->
|
|
20
|
+
|
|
21
|
+
## Considered Options
|
|
22
|
+
|
|
23
|
+
* {title of option 1}
|
|
24
|
+
* {title of option 2}
|
|
25
|
+
* {title of option 3}
|
|
26
|
+
* … <!-- numbers of options can vary -->
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
nav_order: 0
|
|
3
|
+
parent: Decision Records
|
|
4
|
+
---
|
|
5
|
+
# Use Markdown Any Decision Records
|
|
6
|
+
|
|
7
|
+
## Context and Problem Statement
|
|
8
|
+
|
|
9
|
+
We want to record any decisions made in this project independent whether decisions concern the architecture ("architectural decision record"), the code, or other fields.
|
|
10
|
+
Which format and structure should these records follow?
|
|
11
|
+
|
|
12
|
+
## Considered Options
|
|
13
|
+
|
|
14
|
+
* [MADR](https://adr.github.io/madr/) 3.0.0 – The Markdown Any Decision Records
|
|
15
|
+
* [Michael Nygard's template](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions) – The first incarnation of the term "ADR"
|
|
16
|
+
* [Sustainable Architectural Decisions](https://www.infoq.com/articles/sustainable-architectural-design-decisions) – The Y-Statements
|
|
17
|
+
* Other templates listed at <https://github.com/joelparkerhenderson/architecture_decision_record>
|
|
18
|
+
* Formless – No conventions for file format and structure
|
|
19
|
+
|
|
20
|
+
## Decision Outcome
|
|
21
|
+
|
|
22
|
+
Chosen option: "MADR 3.0.0", because
|
|
23
|
+
|
|
24
|
+
* Implicit assumptions should be made explicit.
|
|
25
|
+
Design documentation is important to enable people understanding the decisions later on.
|
|
26
|
+
See also [A rational design process: How and why to fake it](https://doi.org/10.1109/TSE.1986.6312940).
|
|
27
|
+
* MADR allows for structured capturing of any decision.
|
|
28
|
+
* The MADR format is lean and fits our development style.
|
|
29
|
+
* The MADR structure is comprehensible and facilitates usage & maintenance.
|
|
30
|
+
* The MADR project is vivid.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Decisions
|
|
2
|
+
|
|
3
|
+
This directory contains decision records for the project.
|
|
4
|
+
|
|
5
|
+
For new ADRs, please use [adr-template.md](adr-template.md) as basis.
|
|
6
|
+
More information on MADR is available at <https://adr.github.io/madr/>.
|
|
7
|
+
General information about architectural decision records is available at <https://adr.github.io/>.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
---
|
|
2
|
+
nav_order: <%= @index %>
|
|
3
|
+
parent: Decision Records
|
|
4
|
+
|
|
5
|
+
# These are optional elements. Feel free to remove any of them.
|
|
6
|
+
status: {proposed | rejected | accepted | deprecated | … | superseded by ADR-0005 <0005-example.md>}
|
|
7
|
+
date: <%= @decision_date %>
|
|
8
|
+
deciders: {list everyone involved in the decision}
|
|
9
|
+
consulted: {list everyone whose opinions are sought (typically subject-matter experts); and with whom there is a two-way communication}
|
|
10
|
+
informed: {list everyone who is kept up-to-date on progress; and with whom there is a one-way communication}
|
|
11
|
+
---
|
|
12
|
+
# <%= @title %>
|
|
13
|
+
|
|
14
|
+
## Context and Problem Statement
|
|
15
|
+
|
|
16
|
+
{Describe the context and problem statement, e.g., in free form using two to three sentences or in the form of an illustrative story.
|
|
17
|
+
You may want to articulate the problem in form of a question and add links to collaboration boards or issue management systems.}
|
|
18
|
+
|
|
19
|
+
<!-- This is an optional element. Feel free to remove. -->
|
|
20
|
+
## Decision Drivers
|
|
21
|
+
|
|
22
|
+
* {decision driver 1, e.g., a force, facing concern, …}
|
|
23
|
+
* {decision driver 2, e.g., a force, facing concern, …}
|
|
24
|
+
* … <!-- numbers of drivers can vary -->
|
|
25
|
+
|
|
26
|
+
## Considered Options
|
|
27
|
+
|
|
28
|
+
* {title of option 1}
|
|
29
|
+
* {title of option 2}
|
|
30
|
+
* {title of option 3}
|
|
31
|
+
* … <!-- numbers of options can vary -->
|
|
32
|
+
|
|
33
|
+
## Decision Outcome
|
|
34
|
+
|
|
35
|
+
Chosen option: "{title of option 1}", because
|
|
36
|
+
{justification. e.g., only option, which meets k.o. criterion decision driver | which resolves force {force} | … | comes out best (see below)}.
|
|
37
|
+
|
|
38
|
+
<!-- This is an optional element. Feel free to remove. -->
|
|
39
|
+
### Consequences
|
|
40
|
+
|
|
41
|
+
* Good, because {positive consequence, e.g., improvement of one or more desired qualities, …}
|
|
42
|
+
* Bad, because {negative consequence, e.g., compromising one or more desired qualities, …}
|
|
43
|
+
* … <!-- numbers of consequences can vary -->
|
|
44
|
+
|
|
45
|
+
<!-- This is an optional element. Feel free to remove. -->
|
|
46
|
+
## Validation
|
|
47
|
+
|
|
48
|
+
{describe how the implementation of/compliance with the ADR is validated. E.g., by a review or an ArchUnit test}
|
|
49
|
+
|
|
50
|
+
<!-- This is an optional element. Feel free to remove. -->
|
|
51
|
+
## Pros and Cons of the Options
|
|
52
|
+
|
|
53
|
+
### {title of option 1}
|
|
54
|
+
|
|
55
|
+
<!-- This is an optional element. Feel free to remove. -->
|
|
56
|
+
{example | description | pointer to more information | …}
|
|
57
|
+
|
|
58
|
+
* Good, because {argument a}
|
|
59
|
+
* Good, because {argument b}
|
|
60
|
+
<!-- use "neutral" if the given argument weights neither for good nor bad -->
|
|
61
|
+
* Neutral, because {argument c}
|
|
62
|
+
* Bad, because {argument d}
|
|
63
|
+
* … <!-- numbers of pros and cons can vary -->
|
|
64
|
+
|
|
65
|
+
### {title of other option}
|
|
66
|
+
|
|
67
|
+
{example | description | pointer to more information | …}
|
|
68
|
+
|
|
69
|
+
* Good, because {argument a}
|
|
70
|
+
* Good, because {argument b}
|
|
71
|
+
* Neutral, because {argument c}
|
|
72
|
+
* Bad, because {argument d}
|
|
73
|
+
* …
|
|
74
|
+
|
|
75
|
+
<!-- This is an optional element. Feel free to remove. -->
|
|
76
|
+
## More Information
|
|
77
|
+
|
|
78
|
+
{You might want to provide additional evidence/confidence for the decision outcome here and/or
|
|
79
|
+
document the team agreement on the decision and/or
|
|
80
|
+
define when and how this decision should be realized and if/when it should be re-visited and/or
|
|
81
|
+
how the decision is validated.
|
|
82
|
+
Links to other decisions and resources might appear here as well.}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
has_children: true
|
|
3
|
+
---
|
|
4
|
+
# Decision Records
|
|
5
|
+
|
|
6
|
+
We use [Markdown Any Decision Records (MADR)](https://adr.github.io/madr/) version 3.0.0.
|
|
7
|
+
|
|
8
|
+
In general, projects will follow the organisation's way of working and so decisions captured within individual projects will generally cover decisions that:
|
|
9
|
+
|
|
10
|
+
- are not already covered in the Way of Working
|
|
11
|
+
- are covered in the Way of Working, but have specific implementation details which need to be captured
|
|
12
|
+
- diverge from the guidance in the Way of Working
|
|
13
|
+
|
|
14
|
+
## Create a new decision record
|
|
15
|
+
|
|
16
|
+
### Manual approach
|
|
17
|
+
|
|
18
|
+
1. Copy `docs/decisions/adr-template.md` to `docs/decisions/NNNN-title-with-dashes.md`, where `NNNN` indicates the next number in sequence.
|
|
19
|
+
2. Edit `NNNN-title-with-dashes.md`.
|
|
20
|
+
|
|
21
|
+
Note you can also use [other patterns for the directory format](https://github.com/joelparkerhenderson/architecture_decision_record#adr-file-name-conventions).
|
|
22
|
+
As a consequence, some existing tooling might not be applicable.
|
|
23
|
+
|
|
24
|
+
The filenames are following the pattern `NNNN-title-with-dashes.md`, where
|
|
25
|
+
|
|
26
|
+
- `NNNN` is a consecutive number and we assume that there won't be more than 9,999 ADRs in one repository.
|
|
27
|
+
- the title is stored using dashes and lowercase, because [adr-tools] also does that.
|
|
28
|
+
- the suffix is `.md`, because it is a [Markdown](https://github.github.com/gfm/) file.
|
|
29
|
+
|
|
30
|
+
Decisions are placed in the subfolder `decisions/` to keep them close to the documentation but also separate the decisions from other documentation.
|
|
31
|
+
|
|
32
|
+
### Automatic approach
|
|
33
|
+
|
|
34
|
+
To create a new decision record, run:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
way_of_working new decision_record [NAME]
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Where `[NAME]` is the title of your decision record, for example:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
way_of_working new decision_record "Use Markdown Any Decision Records"
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Project decision records
|
|
47
|
+
|
|
48
|
+
Decision records for this project are listed below.
|
data/lib/way_of_working/decision_record/madr/templates/docs/way_of_working/decision-records.md
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: page
|
|
3
|
+
status: REQUIRED
|
|
4
|
+
enforcement: manual
|
|
5
|
+
---
|
|
6
|
+
# Decision Records
|
|
7
|
+
|
|
8
|
+
## Purpose
|
|
9
|
+
|
|
10
|
+
We use [Markdown Any Decision Records (MADR)][madr] v3.0.0 to capture project decisions that:
|
|
11
|
+
|
|
12
|
+
- aren't covered in the [Way of Working][wow]
|
|
13
|
+
- need project-specific implementation details
|
|
14
|
+
- diverge from Way of Working guidance
|
|
15
|
+
|
|
16
|
+
{: .note }
|
|
17
|
+
Proposing and reviewing decisions requires familiarity with [GitHub pull requests][gh-pr].
|
|
18
|
+
|
|
19
|
+
## Scope
|
|
20
|
+
|
|
21
|
+
Applies to decisions about:
|
|
22
|
+
|
|
23
|
+
- **Architecture**: System design, technology stack, frameworks
|
|
24
|
+
- **Process**: Development workflows, deployment, testing
|
|
25
|
+
- **Code standards**: Patterns, conventions, library usage
|
|
26
|
+
- **Dependencies**: Adding/removing major libraries or services
|
|
27
|
+
- **Data**: Schema changes, migration strategies
|
|
28
|
+
|
|
29
|
+
**Create ADR when:**
|
|
30
|
+
|
|
31
|
+
- Impact spans multiple components or teams
|
|
32
|
+
- Long-term consequences or significant cost/risk
|
|
33
|
+
- Multiple viable alternatives exist
|
|
34
|
+
|
|
35
|
+
**Don't create ADR for:**
|
|
36
|
+
|
|
37
|
+
- Already covered by Way of Working
|
|
38
|
+
- Tactical implementation details (use code comments)
|
|
39
|
+
- Temporary workarounds or clear-cut choices
|
|
40
|
+
|
|
41
|
+
## Requirements
|
|
42
|
+
|
|
43
|
+
**File naming:** `NNNN-title-with-dashes.md`
|
|
44
|
+
|
|
45
|
+
- `NNNN` = consecutive four-digit number (0001, 0002, etc.)
|
|
46
|
+
- Lowercase title with dashes
|
|
47
|
+
- Located in `docs/decisions/`
|
|
48
|
+
|
|
49
|
+
**Required content:**
|
|
50
|
+
|
|
51
|
+
1. Status (proposed, accepted, rejected, deprecated, superseded)
|
|
52
|
+
2. Context and problem statement
|
|
53
|
+
3. Considered options (minimum 2)
|
|
54
|
+
4. Decision outcome with justification
|
|
55
|
+
5. Positive and negative consequences
|
|
56
|
+
|
|
57
|
+
**Status lifecycle:**
|
|
58
|
+
|
|
59
|
+
- **proposed** - Under discussion
|
|
60
|
+
- **accepted** - Approved for implementation
|
|
61
|
+
- **rejected** - Decided against
|
|
62
|
+
- **deprecated** - No longer relevant
|
|
63
|
+
- **superseded** - Replaced by newer ADR (link required)
|
|
64
|
+
|
|
65
|
+
## Setup
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
way_of_working init decision_record
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Usage
|
|
72
|
+
|
|
73
|
+
### Create a new decision record
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
way_of_working new decision_record "Use Markdown Any Decision Records"
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**Propose decision:**
|
|
80
|
+
|
|
81
|
+
1. Create ADR with status "proposed"
|
|
82
|
+
2. Open PR, tag stakeholders
|
|
83
|
+
3. Discuss in PR comments
|
|
84
|
+
4. Update to "accepted" once consensus reached
|
|
85
|
+
5. Merge PR
|
|
86
|
+
|
|
87
|
+
**Supersede existing ADR:**
|
|
88
|
+
|
|
89
|
+
1. Create new ADR
|
|
90
|
+
2. Update old ADR status to "superseded by [0XXX-new-title.md](0XXX-new-title.md)"
|
|
91
|
+
3. Explain what changed in new ADR
|
|
92
|
+
|
|
93
|
+
## Enforcement
|
|
94
|
+
|
|
95
|
+
**PR Review:**
|
|
96
|
+
|
|
97
|
+
- All ADRs submitted via PR
|
|
98
|
+
- Maintainer approval required
|
|
99
|
+
- Verify MADR template structure, file naming, location
|
|
100
|
+
- Check minimum 2 options with justification
|
|
101
|
+
|
|
102
|
+
**Quality checklist:**
|
|
103
|
+
|
|
104
|
+
- ✅ Multiple alternatives with pros/cons
|
|
105
|
+
- ✅ Clear context and constraints
|
|
106
|
+
- ✅ Explicit trade-offs
|
|
107
|
+
- ❌ Only one option presented
|
|
108
|
+
- ❌ Missing justification or context
|
|
109
|
+
|
|
110
|
+
**Maintenance:**
|
|
111
|
+
|
|
112
|
+
- Quarterly review for relevance
|
|
113
|
+
- Update deprecated/superseded status as needed
|
|
114
|
+
|
|
115
|
+
## Examples
|
|
116
|
+
|
|
117
|
+
**Database choice:**
|
|
118
|
+
|
|
119
|
+
```markdown
|
|
120
|
+
# Use PostgreSQL for Primary Database
|
|
121
|
+
|
|
122
|
+
**Status:** accepted
|
|
123
|
+
**Date:** 2025-10-15
|
|
124
|
+
|
|
125
|
+
## Context and Problem Statement
|
|
126
|
+
|
|
127
|
+
Need relational database for user data with complex relationships.
|
|
128
|
+
Constraints: ACID compliance, 100K users/1M records, prefer open-source.
|
|
129
|
+
|
|
130
|
+
## Considered Options
|
|
131
|
+
|
|
132
|
+
1. PostgreSQL (self-hosted)
|
|
133
|
+
2. MySQL
|
|
134
|
+
3. RDS PostgreSQL
|
|
135
|
+
|
|
136
|
+
## Decision Outcome
|
|
137
|
+
|
|
138
|
+
**Chosen:** PostgreSQL (self-hosted)
|
|
139
|
+
|
|
140
|
+
**Pros:** Open-source, advanced features (JSON, full-text search), ACID, team experience
|
|
141
|
+
**Cons:** DevOps overhead, manual backup, manual scaling
|
|
142
|
+
|
|
143
|
+
**Why:** Meets requirements within budget. Cloud-managed exceeds budget by 40%.
|
|
144
|
+
Team expertise mitigates complexity.
|
|
145
|
+
|
|
146
|
+
**Alternatives rejected:**
|
|
147
|
+
- MySQL: No advantage over PostgreSQL
|
|
148
|
+
- RDS: Cost $200-500/month exceeds budget
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**Superseded ADR:**
|
|
152
|
+
|
|
153
|
+
```markdown
|
|
154
|
+
# Use Jest for Frontend Testing
|
|
155
|
+
|
|
156
|
+
**Status:** superseded by [0015-use-vitest.md](0015-use-vitest.md)
|
|
157
|
+
**Date:** 2025-01-10 (original), 2025-09-15 (superseded)
|
|
158
|
+
|
|
159
|
+
**Why superseded:** Vitest now standard for Vite projects,
|
|
160
|
+
better integration and performance.
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
**Rejected decision:**
|
|
164
|
+
|
|
165
|
+
```markdown
|
|
166
|
+
# Use GraphQL for API Layer
|
|
167
|
+
|
|
168
|
+
**Status:** rejected
|
|
169
|
+
**Date:** 2025-03-20
|
|
170
|
+
|
|
171
|
+
## Decision Outcome
|
|
172
|
+
|
|
173
|
+
**Chosen:** Continue with REST API
|
|
174
|
+
|
|
175
|
+
**Why rejected:**
|
|
176
|
+
- Current REST meets all requirements
|
|
177
|
+
- Team lacks GraphQL expertise (4-6 week learning curve)
|
|
178
|
+
- Migration cost: 8 engineer-weeks
|
|
179
|
+
- No client needs GraphQL features
|
|
180
|
+
|
|
181
|
+
**Reconsider when:** Multiple clients need flexible fetching
|
|
182
|
+
or over-fetching impacts performance.
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Resources
|
|
186
|
+
|
|
187
|
+
- [MADR Documentation][madr]
|
|
188
|
+
- [GDS Architecture Decisions][gds-way]
|
|
189
|
+
- [Way of Working CLI][wow-cli]
|
|
190
|
+
|
|
191
|
+
[madr]: https://adr.github.io/madr/
|
|
192
|
+
[wow]: https://github.com/HealthDataInsight/way_of_working
|
|
193
|
+
[gds-way]: https://gds-way.digital.cabinet-office.gov.uk/standards/architecture-decisions.html
|
|
194
|
+
[gh-pr]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests
|
|
195
|
+
[wow-cli]: ../cli.md
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'madr/generators/init'
|
|
4
|
+
require_relative 'madr/generators/new'
|
|
5
|
+
|
|
6
|
+
# If way_of_working-audit-github is used we can add a rule
|
|
7
|
+
begin
|
|
8
|
+
require 'way_of_working/audit/github/rules/registry'
|
|
9
|
+
require_relative 'madr/github_audit_rule'
|
|
10
|
+
rescue LoadError # rubocop:disable Lint/SuppressedException
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
module WayOfWorking
|
|
14
|
+
module DecisionRecord
|
|
15
|
+
module Madr
|
|
16
|
+
class Error < StandardError; end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
module SubCommands
|
|
21
|
+
# This reopens the "way_of_working init" sub command
|
|
22
|
+
class Init
|
|
23
|
+
register(DecisionRecord::Madr::Generators::Init, 'decision_record', 'decision_record',
|
|
24
|
+
<<~LONGDESC)
|
|
25
|
+
Description:
|
|
26
|
+
This generator adds Markdown Any Decision Records (MADR) to your project
|
|
27
|
+
|
|
28
|
+
Example:
|
|
29
|
+
way_of_working init decision_record
|
|
30
|
+
|
|
31
|
+
This will create:
|
|
32
|
+
docs/decisions/README.md
|
|
33
|
+
docs/decisions/adr-template.md
|
|
34
|
+
docs/decisions/0000-use-markdown-any-decision-records.md
|
|
35
|
+
LONGDESC
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# This reopens the "way_of_working new" sub command
|
|
39
|
+
class New
|
|
40
|
+
register(DecisionRecord::Madr::Generators::New, 'decision_record', 'decision_record [NAME]',
|
|
41
|
+
<<~LONGDESC)
|
|
42
|
+
Description:
|
|
43
|
+
This generator adds a new decision record to your project
|
|
44
|
+
|
|
45
|
+
Example:
|
|
46
|
+
way_of_working new decision_record "Title of the decision"
|
|
47
|
+
|
|
48
|
+
This will create:
|
|
49
|
+
docs/decisions/NNNN-title-of-the-decision.md
|
|
50
|
+
LONGDESC
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -58,10 +58,10 @@ module WayOfWorking
|
|
|
58
58
|
|
|
59
59
|
def likely_upstream_remote
|
|
60
60
|
remotes = @base.remotes
|
|
61
|
-
return remotes.first if remotes.
|
|
61
|
+
return remotes.first if remotes.one?
|
|
62
62
|
|
|
63
63
|
remotes = remotes.reject { |remote| remote.name == 'origin' }
|
|
64
|
-
return remotes.first if remotes.
|
|
64
|
+
return remotes.first if remotes.one?
|
|
65
65
|
|
|
66
66
|
remotes.last
|
|
67
67
|
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'thor'
|
|
4
|
+
require 'rainbow'
|
|
5
|
+
|
|
6
|
+
module WayOfWorking
|
|
7
|
+
module InclusiveLanguage
|
|
8
|
+
module Alex
|
|
9
|
+
module Generators
|
|
10
|
+
# This generator runs the inclusive language tests
|
|
11
|
+
class Exec < Thor::Group
|
|
12
|
+
argument :path, type: :string, required: false, desc: 'Optional path of the file to test'
|
|
13
|
+
|
|
14
|
+
desc 'This runs the inclusive language tests on this project'
|
|
15
|
+
|
|
16
|
+
def run_first
|
|
17
|
+
@start_time = Time.now
|
|
18
|
+
|
|
19
|
+
say(Rainbow("Limiting tests to #{path}\n").yellow) if path
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def prep_and_run_alex
|
|
23
|
+
command = ['npx', 'alex', '--why']
|
|
24
|
+
# Configure alex to only test a specific file or folder, if defined
|
|
25
|
+
command << path if path
|
|
26
|
+
|
|
27
|
+
say(Rainbow("\nRunning alex...").yellow)
|
|
28
|
+
|
|
29
|
+
@alex_ok = run_alex(command)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def run_last
|
|
33
|
+
say(Rainbow("\nTotal time taken: #{(Time.now - @start_time).to_i} seconds").yellow)
|
|
34
|
+
|
|
35
|
+
if @alex_ok
|
|
36
|
+
say(Rainbow("\nInclusive language tests succeeded!").green)
|
|
37
|
+
else
|
|
38
|
+
abort(Rainbow("\nInclusive language tests failed!").red)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def run_alex(arguments)
|
|
45
|
+
system(*arguments)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'thor'
|
|
4
|
+
|
|
5
|
+
module WayOfWorking
|
|
6
|
+
module InclusiveLanguage
|
|
7
|
+
module Alex
|
|
8
|
+
module Generators
|
|
9
|
+
# This generator adds the alexrc file
|
|
10
|
+
class Init < Thor::Group
|
|
11
|
+
include Thor::Actions
|
|
12
|
+
|
|
13
|
+
source_root WayOfWorking.root.join('lib', 'way_of_working', 'inclusive_language', 'alex', 'templates')
|
|
14
|
+
|
|
15
|
+
def copy_inclusive_language_github_workflow_action
|
|
16
|
+
copy_file '.github/workflows/inclusive-language.yml'
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def copy_alexignore_file
|
|
20
|
+
copy_file '.alexignore'
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def copy_alexrc_file
|
|
24
|
+
copy_file '.alexrc'
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def copy_way_of_working_documentation
|
|
28
|
+
copy_file 'docs/way_of_working/inclusive-language.md'
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|