github.com/joshdk/godel@v0.0.0-20170529232908-862138a45aee/docs/Philosophy.md (about)

     1  Philosophy
     2  ==========
     3  The design of gödel is based on some core philosophical principles. Although they are not particularly novel, these
     4  principles drive most of the design decisions of gödel.
     5  
     6  Builds and deployment are part of a project
     7  -------------------------------------------
     8  Building and deployment should be core concerns for all projects. Code is not useful in a vacuum, and it is all too
     9  often the case that developers focus on writing code or implementing functionality without giving deep thought to how
    10  the software will eventually be built and deployed. Building the binaries for a product and creating its distribution is
    11  just as critical to a product as its core code, and the code and mechanisms for performing these tasks should be held to
    12  the same standard as the core code for a project.
    13  
    14  gödel provides tasks for building and publishing and includes them as part of the core project.
    15  
    16  Separate configuration and logic
    17  --------------------------------
    18  Configuration and logic should be distinct. The place where this is violated most often is build scripts -- typically,
    19  scripts that are written to build and distribute a project conflate the configuration for the project with the logic for
    20  performing actions such as building and publishing the product. This makes it hard to track changes, since changes in
    21  these files may be either due to changing configuration or changing logic. It also makes logic harder to test and
    22  re-use. If a script is useful, other projects will tend to copy them. However, if they mingle logic and configuration,
    23  they tend to fork as they are copied, and it's hard (or impossible) to roll out updates later in a uniform manner.
    24  
    25  gödel establishes clear separation between configuration and logic. All of the project-specific configuration is stored
    26  in config files in `godel/config` and all of the generic logic for running actions is in gödel. This allows the logic to
    27  be tested and generalized, and also makes it possible to roll out updates cleanly across multiple different projects.
    28  
    29  If it's not automatically enforced, it's not a convention
    30  ---------------------------------------------------------
    31  Whether it be indentation level, coding style, license headers on files or tasks that create generated code, if a
    32  practice is not enforced in an automated manner, it's not a convention. This is similar to the philosophy behind
    33  [gofmt](https://blog.golang.org/go-fmt-your-code). Establishing a convention and asking people to follow it (by running
    34  the program themselves, creating a Git hook for it, etc.) is great. However, if the convention is not checked and
    35  enforced in an automated manner, it is doomed to fail (a.k.a. [second law of thermodynamics](https://en.wikipedia.org/wiki/Second_law_of_thermodynamics)).
    36  
    37  gödel tasks are designed to be able to run as part of CI to verify and enforce best practices for formatting, linting
    38  checks, license headers and more.
    39  
    40  Tasks that apply should be able to verify
    41  -----------------------------------------
    42  Some tasks like applying correct formatting or ensuring that source files have a specific license header can be applied
    43  automatically without user feedback. Ideally, any task that can apply a change should also have a mode that verifies
    44  whether the state of the world matches the expected state without making any modifications. If the state of the world
    45  differs, the task should fail and provide feedback about what needs to be changed to make it pass. Having this mode of
    46  operation makes it much easier to run the task as part of continuous integration.
    47  
    48  gödel provides a verification mode for all of its tasks that make modifications.
    49  
    50  Optimize the default use case for humans
    51  ----------------------------------------
    52  When thinking about the default behavior for commands or flags, optimize on the choice that is easier for humans that
    53  use the tool in an interactive manner. This came up when trying to determine the default behavior for `./godelw verify`.
    54  The command has an `--apply` flag that, when true, applies the changes detected by verify, and there was a question as
    55  to what the default value of the flag should be. Conceptually, there's a case to be made for the default value being
    56  false -- `verify` should only verify by default and apply changes only when specifically instructed to do so. However,
    57  most developers invoking the command locally will always want to apply the changes that are flagged. Requiring them to
    58  run `./godelw verify --apply=true` every time is much more onerous than just making `./godelw verify` apply changes by
    59  default. Based on this, we made the default value of the flag `true`. This means that CI environments typically have to
    60  invoke `./godelw verify --apply=false`. However, in CI, this value is defined once and then forgotten, so this trade-off
    61  made sense.
    62  
    63  gödel is designed to have sensible defaults that do what a developer would expect when invoking the command locally.
    64  
    65  Checks must be repeatable
    66  -------------------------
    67  The result of a given set of checks on a specific input should stay constant over time. This property is not true for
    68  builds that use `go get` to retrieve checks or build tools at the beginning of each build because the result of `go get`
    69  can change over time (either because the version of the tool changes or because it becomes unavailable).
    70  
    71  gödel contains the code for all of its checks and requires projects to declare the version of gödel that is uses as part
    72  of its configuration, so a given version of a project is tied to a specific version of gödel and the result of the
    73  checks stay constant over time.
    74  
    75  Although the gödel executable itself is not included in projects, as long as the distribution for the version of gödel
    76  used is available it will build with the same result. gödel distributions can also be downloaded/saved locally or
    77  on-prem to avoid dependencies on external services.
    78  
    79  Checks should be fast and idempotent
    80  ------------------------------------
    81  Developers iterate quickly and have little patience. Tasks should complete on the order of seconds when possible. It
    82  should also be possible to kill checks at any point without any adverse effects.
    83  
    84  Most gödel tasks complete in under 1 second on small projects, and even on larger projects most tasks complete on the
    85  order of seconds (checks and tests can take longer). All tasks are designed to be idempotent and fail gracefully and
    86  clean up when terminated.
    87  
    88  Failures should be obvious and provide contextual information
    89  -------------------------------------------------------------
    90  If a task or operation fails, it should provide a user-readable explanation of what failed along with any relevant
    91  context for diagnosing or reproducing the issue.
    92  
    93  gödel strives to provide readable error messages with context rather than just stack traces on failures. Common failure
    94  paths have been identified and the error messages strive to include common causes and work-arounds. Failures that occur
    95  in sub-processes include information on the command that was invoked and the environment variables provided to it so
    96  that users can attempt to diagnose the issue manually.
    97  
    98  Provide building blocks that can be composed
    99  --------------------------------------------
   100  Most tasks are composed of multiple different parts or actions, and by default tasks should provide an all-inclusive
   101  experience that works out-of-the-box. However, tasks should also provide the flexibility to be used in other ways so
   102  other tools can use them to compose their own tasks. When possible, internal tasks should also be structured in a way
   103  that composes these distinct tasks so that it is possible to manually perform partial tasks or debug failures of
   104  compound tasks.
   105  
   106  gödel provides tasks such as `./godelw packages` and `./godelw products` that echo out state defined by gödel in a
   107  manner that can easily be consumed by other tasks or tools. It also provides the `__` invocation mechanism that can be
   108  used to run individual pieces of subprograms in isolation (for example, running `./godelw __check __errcheck` invokes
   109  the exact piece of sub-functionality used by `./godelw check` when it runs `errcheck`).
   110  
   111  Use a single source of truth with good abstractions
   112  ---------------------------------------------------
   113  If there is state about the world that is true, define it in a single place and re-use it across the project using the
   114  correct abstractions. For example, most build tools want to ignore the "vendor" directory and any directories that
   115  contain generated code. These directories should be excluded from tests, checks, formatting, etc. Rather than adding
   116  logic to each of these different tasks to ignore these directories, recognize that the abstract issue is that there are
   117  a set of directories that are not considered part of the core source of a project -- define them in a single place and
   118  then share that information across all tasks and logic.
   119  
   120  gödel does this for things like exclude directories and projects. It establishes abstractions for things like products,
   121  projects and packages and uses them consistently in its own code and exposes them as tasks so that other tools can use
   122  them as well.
   123  
   124  Orchestrate, don't obfuscate
   125  ----------------------------
   126  Go has standard tooling that people understand well. Whenever possible, build tools should delegate to the built-in
   127  tooling to do things in a standard manner.
   128  
   129  gödel is designed to do as little work as possible -- its main concerns are reading declarative configuration and then
   130  orchestrating standard tools to perform the actual work. When errors or failures occur, gödel strives to expose the
   131  failure in a way that can be repeated/verified using standard tooling to verify that the failure was not introduced by
   132  gödel.
   133  
   134  If it's not tested, it can't be trusted
   135  ---------------------------------------
   136  If a piece of functionality isn't tested, it can't be trusted to work. Even if it works now, there are no guarantees
   137  that the behavior won't regress in the future.
   138  
   139  gödel has extensive unit and integration tests that are run in CI against multiple different environments. Almost all
   140  fixes for issues that are identified are accompanied by tests that ensure that the issue stays fixed. The code and build
   141  is designed in a manner that almost every aspect is testable.
   142  
   143  Anticipate user needs and enable them
   144  -------------------------------------
   145  If a set of checks are run and one fails, a user will probably want the ability to re-run just the failing check. If
   146  the "build" task builds all products for all platforms by default, a user will probably want to be able to build just a
   147  specific product for a specific platform. If a task caches results by default, the user will probably want a way to run
   148  the task in a manner that ignores the cache.
   149  
   150  gödel tries to anticipate all of the needs or requests that a user will have for a task enable them. The goal is to
   151  optimize for the common case, but to provide configuration and options for customizing behavior as necessary.
   152  
   153  Trust is hard to earn and easy to lose
   154  --------------------------------------
   155  Having someone opt to use a piece of software is one of the highest compliments that can be paid to it. People are
   156  inherently skeptical of new products, and nothing is more frustrating than a build tool that becomes a source of build
   157  issues. Build tools are often the bearer of bad news so people tend to have a negative reaction towards interacting with
   158  them in the first place, so people have very little patience with build tools.
   159  
   160  gödel strives to be a tool that enhances productivity and can be trusted as a core part of a development and continuous
   161  integration setup. It is dog-fooded extensively and maintained with care.