declapract v0.12.0
declapract
Scalable software best practices. Declare, plan, and apply software practices across code bases.
Table of Contents
Purpose
Scaling software best practices across an organization is difficult - but when done right, is a super power!

declapract provides a declarative, scalable, and maintainable way to define, enforce, and evolve software best practices to unlock:
- systematic knowledge synthesis
- practices are declaratively defined with code and explained with readmes
- so that
- future travelers can read exactly why a practice was deemed "best" or "bad"
- future travelers can collaborate on, debate, improve, and manage software practices like code (e.g., with pull requests, issues, etc)
- automatic knowledge transfer
- developers in your org are alerted any time they don't follow a best practice - or use a bad practice!
- so that
- knowledge is not siloed to people that have learned through tribal knowledge already what practices are best or bad
- knowledge can be transferred automatically as part of the automated software development lifecycle (e.g., as part of your cicd-pipeline, exposed in pull-requests)
- effortless code base monitoring
- determine for any code base whether and which software practices it is not adhering to with one command
- so that
- you can automatically block code changes that introduce bad practices / don't follow best practices
- you can easily get check each code base in your organization to get an overview of technical debt
- scalable technical debt elimination
- automatically fix code bases, upgrading them to your best practices and removing bad practices
- so that
- you can scale keeping your code bases up to date
- your developers can get back to adding business value instead of upgrading old project
Software practices are an infrastructure of their own. Its time we manage them like it.
Usage
There are a few cases for using declapract. We'll go over each.
Case 1: Create a new code base from declared best practices
Similar to git clone, but leveraging explicitly declared best practices to create an entirely new project instead.
Example:
npx declapract clone --declarations=npm:module-with-your-declarations --use-case=your-use-caseCase 2: Add best practice management to a code base
Sets up a code base to follow a set of declared best practices.
install
npm install --save-dev declapractconfigure
touch ./declapract.use.yml;
echo "
declarations: npm:module-with-your-practices
useCase: your-use-case
variables:
variableName: 'variableValue' # the variables required by the practices
...
" >> ./declapract.use.yml;Case 3: Declare best practices
First, you'll need to create a new repository that will house your declared practices. The best way to do this is to clone our best practices for declaring best practices 😄
npx declapract --declarations=ssh:github.com/uladkasach/best-practices-declarations --use-case=declareNext, you'll need to declare your practices, use-cases, and examples. See the docs on declarations to learn how to do this.
Commands
declapract apply
apply fixes to all files which have failed to adhere to any of the project's declared practices and have an automatic fix available.
USAGE
$ declapract apply
OPTIONS
-c, --config=config (required) [default: declapract.use.yml] path to the declapract usage config yml
-f, --file=file the file path of a specific file you want to scope checking for
-h, --help show CLI help
-p, --practice=practice the name of a specific practice you want to scope checking forSee code: dist/contract/commands/apply.ts
declapract compile
compile the declared declarations so that they can be packaged and distributed by npm safely
USAGE
$ declapract compile
OPTIONS
-d, --distributionDirectory=distributionDirectory (required) [default: dist] the distribution directory to which we
will compile the declarations
-h, --help show CLI help
-s, --sourceDirectory=sourceDirectory (required) [default: src] the source directory which contains the
declarations to compileSee code: dist/contract/commands/compile.ts
declapract help [COMMAND]
display help for declapract
USAGE
$ declapract help [COMMAND]
ARGUMENTS
COMMAND command to show help for
OPTIONS
--all see all commands in CLISee code: @oclif/plugin-help
declapract plan
plan and display what actions need to be taken in order to make a software project adhere to its declared practices.
USAGE
$ declapract plan
OPTIONS
-c, --config=config (required) [default: declapract.use.yml] path to the declapract usage config yml
-f, --file=file the file path of a specific file you want to scope checking for
-h, --help show CLI help
-p, --practice=practice the name of a specific practice you want to scope checking forSee code: dist/contract/commands/plan.ts
declapract validate
validate the declared practices, use cases, and examples; checks that these declarations are usable and don't contain declaration errors
USAGE
$ declapract validate
OPTIONS
-c, --config=config (required) [default: declapract.declare.yml] path to the declapract declarations config yml
-h, --help show CLI helpSee code: dist/contract/commands/validate.ts
Philosophy
declarative > imperative
define what you want/dont-want to see, not how to check it
why? maintainability! declarative code is much easier to read, write, and collaborate on
note:
- although
declapractstill lets you imperatively define customcheckfunctions, you should be writing your checks declaratively in most cases. - if you find a common use case that you can't write declaratively, open up an issue so we can get it supported!
automatic fixes for everything
files failing checks should have a fix that can be automatically applied
why? speed, quality, and happiness: computers are much better at, faster at, and happier to do repetitive tasks than humans
note:
- declapract automatically defines fixes for you on as many
FileCheckTypesas possible, but you may still need to explicitly define the fix in some cases yourself - if you find a common use case that you have to write custom fixes for, open up an issue so we can get it supported!
Declarations
declapract enables you to define your best-practices and bad-practices by showing examples of what you want/dont-want files to be like in the projects you're checking.
For example, a file structure such as this:
- src/
- practices/
- terraform/
- best-practice/
- .terraform-version
- terraform/
- modules/
- main.tf.declapract.ts
- sqs.tf.declapract.ts
- environments/
- prod/
- main.tf
- main.tf.declapract.ts
- dev
- main.tf
- main.tf.declapract.ts
- bad-practices/
- tfvars/
- terraform/
- **/
- *.tfvars.declapract.tstells us that we have one best-practice and one bad-practice defined.
The best-practice directory declares an example of what a project following the best practice looks like, with some declapract metadata files that allow us to customize what to check for.
The best-practice, in this example, checks several files. For example:
.terraform-versionhas its expected contents declared- which means that projects using this practice will need
- a file with path
.terraform-versionto exist (it is required, by default) - the file the match the exact file contents declared here (the check type is FileCheckType.EXACT, when file contents are declared, by default)
- a file with path
- which means that projects using this practice will need
terraform/modules/main.tfhas a metadata file declared for it,terraform/modules/main.tf.declapract.tf- this could mean multiple things, as the metadata file gives you lots of customization over what to check
- for example:
- there may be a custom check defined
- the file may just need to be checked for existence
- see more details about the file-check-metadata file below
terraform/environments/prod/main.tfhas both: a file declaring expected contents and a metadata file- the fact that the user declared expected contents means that they likely want to use them in the check
- the presence of the metadata file could mean many things
- the user may have just wanted to define a custom
fixfunction - the user may have wanted to check that the file
containsthe declared contents, instead of the defaultexact equalscheck - the user may have also wanted to specify that the existence of this file is optional
- the user may have just wanted to define a custom
- we'd have to read the contents of the file-check-metadata file to find out for sure, but these are the options above
The bad-practice directory, on the other hand, declares an example of what not to do; i.e., what a project that followed this bad practice would look like. It too includes declapract metadata files in order to customize what to check for, for each file.
The bad-practice bad-practices/tfvars, in this example, can check many files even though it only declares one checks:
terraform/**/*.tfvarsis the glob pattern for the files to check declared by thisbad-practice- this means that the check defined here will apply to all files that match the
terraform/**/*.tfvarsglob pattern, if any
- this means that the check defined here will apply to all files that match the
Practices
Practices are coding patterns that you either want to require in projects - or forbid in projects. Practices are defined in a declarative way by defining the files that are relevant to the practice and how to check them. Practices are defined through the example of a best-practice or one or more bad-practices (or both).
The name of a practice is defined by the name of the directory its declarations live in. For example, a directory named practices/terraform/ would house the terraform practice.
Practices can have up to one best-practice and any number of bad-practices declared. These, too, are identified by directory structure. For example, practices/terraform/best-practice will house the best-practice declaration for terraform - and practices/terraform/bad-practices/tfvars would house a bad-practice declaration.
Both best-practice and bad-practice declarations are defined in the same way, by declaring an example of a project that should be matched against. In other words, you'll define the files you want to check by defining them just like you would in a real project - and you'll define how to check them by declaring their contents, metadata, or a custom check function.
File Check Declaration Examples
FileCheckType.EQUALS
The most straight forward file check type is the "exact contents" file check. Its used to check that a file's contents exactly equal the contents you declare.
To define one of these, simply declare the contents of the file you want to check.
For example:
- declare
practices/terraform/best-practice/.terraform-versionas:0.14.11 - to check that:
- a file named
.terraform-versionis defined at path<root>/.terraform-versionand that - its contents exactly equal what is declared in that file:
0.14.11.
- a file named
FileCheckType.CONTAINS
Another common file check type is the "contains contents" file check. Its used to check that a file's contents contain the contents you declare.
To define one of these, declare the contents that you want to see contained in the file matching the file path - and then additionally define a file-check-metadata file for that file's path and use it to specify that the check type should be CONTAINS.
For example:
- declare
practices/git/best-practice/.gitignoreasdist node_modules coverage .serverless .env .terraform .terraform.lock and declare
practices/git/best-practice/.gitignore.declapract.tsasimport { FileCheckType } from 'declapract'; export const check = FileCheckType.CONTAINS;to check that:
- a file named
.gitignoreis defined at path<root>/.gitignore - its contents contain what is declared in
practices/git/best-practice/.gitignore
- a file named
@declapract{variable}s in declaration files
Declapract supports referencing project variables in declaration files.
For example:
declare the variables for your project in your
declapract.use.ymlconfigvariables: serviceName: 'svc-awesomeness' organizationName: 'all-the-things-corp'and declare a file check that references these variables,
practices/npm/best-practice/README.md# @declapract{variable.serviceName} this is the repo for `@declapract{variable.serviceName}` of org `@declapract{variable.organizationName}` this readme contains all of the relevant details needed to be known about @declapract{variable.serviceName}to check that:
- a file named
README.mdis defined at path<root>/README.mdand that it contains the contents of:
# svc-awesomeness this is the repo for `svc-awesomeness` of org `all-the-things-corp` this readme contains all of the relevant details that are needed to be known about svc-awesomeness
- a file named
FileCheckType.CONTAINS on a .json file
A more specific case of the "contains check" is when it applies to a JSON file. Declapract defaults to using a special contains check function which checks that each key-value pair of the found json object and the declared json objects match, instead of doing a simple "contains substring" check like normal.
For example:
- declare
practices/prettier/best-practice/package.jsonas{ "name": "@declapract{variable.serviceName}", "devDependencies": { "prettier": "@declapract{check.minVersion('2.0.0')}" }, "scripts": { "format": "prettier --write 'src/**/*.ts'" } } and declare
practices/prettier/best-practice/package.jsonasimport { FileCheckType } from 'declapract'; export const check = FileCheckType.CONTAINS;to check that:
- a file named
package.jsonis defined at path<root>/package.json - that it contains a json object
- that the json object has a key
namedefined as theserviceNamedeclared in your project's variables- note that this part employs the use of a declapract
variable
- note that this part employs the use of a declapract
- that the json object has key
scripts.formatdefined as"prettier --write 'src/**/*.ts' - that the json object has key
devDependencies.prettierdefined as a version which is greater than or equal to2.0.0- note that this part employs the use of a declapract
check expression
- note that this part employs the use of a declapract
- a file named
FileCheckType.EXISTS
Another common file check type is the "exists" file check. As you'd expect, its used to check whether a file exists.
To define one of these, simply define a file-check-metadata file for that file's path and specify that the check type should be EXISTS.
For example, for defining a best practice:
declare
practices/npm/best-practice/package-lock.json.declapract.tsas:import { FileCheckType } from 'declapract'; export const check = FileCheckType.EXISTS;to check that:
- a file with path
practices/npm/best-practice/package-lock.jsonexists
- a file with path
For example, for defining a bad practice:
declare
practices/directory-structure/bad-practices/models-dir/src/models/**/*.ts.declapract.tsas:import { FileCheckType } from 'declapract'; export const check = FileCheckType.EXISTS;to check that:
- no files match the path
<root>/src/models/**/*.ts - note that in this example we specified a
bad-practice, which is why declapract will make sure that files which match theEXISTScheck for this path do not exist.
- no files match the path
FileCheckType.CUSTOM
When the above checks don't meet your needs, you are able to declare a custom check for files that match a file path.
To define one of these, simply define a file-check-metadata file for that file's path and specify a custom check function.
Throw an error if the file does not pass the check - or return nothing if it does.
For example:
declare
practices/npm/best-practice/package.json.declapract.tsimport { FileCheckFunction, FileCheckContext } from 'declapract'; export const check: FileCheckFunction = (contents: string | null, context: FileCheckContext) => { // anything way you want to check }
To Do
todo: update the readme to better document how to define FileCheckDeclarations and PracticeDeclarations
- fixes
- more clear examples
- best practice -vs- bad practices
- syntax on defining custom check functions
- etc
Contribution
Team work makes the dream work! Please create a ticket for any features you think are missing and, if willing and able, draft a PR for the feature :)
1 year ago
1 year ago
1 year ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago