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: .. code-block:: haskell :emphasize-lines: 3 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: .. code-block:: haskell :emphasize-lines: 2 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: .. code-block:: haskell :emphasize-lines: 2 optionInputValue :: Entity VacancyFilterOption -> Text optionInputValue (Entity optionK _) = toPathPiece optionK to the following: .. code-block:: haskell :emphasize-lines: 2 optionInputValue :: VacancyFilterOptionId -> Text optionInputValue optionK = toPathPiece optionK