Coding Guidelines

In general do the following:

  • Focus on readabilty when writing code: readable code is easier to understand, thus easier to maintain, modify, and debug. Write code that reads from left-to-right and from top-to-bottom.

  • To improve maintainability (see “Building Maintainable Software” by Joost Visser):

    • write short units of code

    • write simple units of code

    • write code once

    • keep unit interfaces small

    • separate concerns in modules

    • automate tests

    • write clean code

    • write all possible scenarios of a case statement

Imports

Use qualified imports for everything but the following:

  • ClassyPrelude

Other imports should either be qualified, or all identifiers must be imported explicitly.

Models, Lenses, and Record Dot Syntax

Use lenses to get and set (model) record fields. Usage of record dot syntax has been deprecated.

When using lenses: prefer operators over functions (e.g. ^. over view), except when partially applying lens expressions (e.g. fmap (view userName)).

Naming

Always Name the Return Value of getYesod app

Always use the following:

settings :: Handler PostmarkSettings
settings = do
  app <- getYesod
  pure
    ( PostmarkSettings
        { apiUrl = "https://api.postmarkapp.com/email/withTemplate/",
          apiToken = (EmailSettings.postmarkApiKey <. appEmailSettings <. appSettings)  app
        }
    )

Database

Indices

Use the following naming scheme: $table_$column_idx, e.g. an index for the version_id column on the component table must have the name component_version_id_idx.

Destructuring Entities

Use the following naming scheme:

optionInputValue :: Entity VacancyFilterOption -> Text
optionInputValue optionE@(Entity optionK option) =
  ...

Nested Case and If Expressions

Avoid nested case and if expressions by defining additional functions, using monads and monad transformers.

Operators

$

Do not use $, just use parens.

<|

Do not use <| (from the Flow package), just use parens.

=<<

Do not use =<<, just use >>=.

|>

Do not use |> (from the Flow package), just use parens.

Prefer To Pass and Receive Complete Entities

For consistency pass and receive complete entities instead of just entity keys or just entity values (if possible). Thus prefer the following:

optionInputValue :: Entity VacancyFilterOption -> Text
optionInputValue (Entity optionK _) =
  toPathPiece optionK

to the following:

optionInputValue :: VacancyFilterOptionId -> Text
optionInputValue optionK =
  toPathPiece optionK