Skip to content

HOWTO dev best_practices

steveoro edited this page Apr 27, 2021 · 4 revisions

HOWTO: Development Best-practices

References:

We won't waste words to endorse further or evangelize TDD/BDD: for us it's simply the way to go.

If you're still in doubt, just ask Steve a brief comment about it and he shall entertain you for the rest of the day. ...Ah! True story! 🍷

Develop-debug-commit flow:

Auto-testing is important as much as your code versioning system: we prefer RSpec over the rest.

  • Write the descriptions in your RSpec tests so that once you run the whole suite with an rspec -f doc, the output is in readable English and descriptive enough for the expected behavior of the different parts of the applications and their use-case.

  • Focus on one use-case at a time, and try to describe it thoroughly. (See the various references above on how to write better specs.)

  • Always code with a background running guard in a console as delineated in Guard & Zeus/Spring setup and usage, so that the code gets tested as soon as you write and save it.

  • Remember to do a git pull before starting a new coding session, in case someone else wrote additional branches or pull requests. "Just to stay in the loop."

  • Commit early & commit often. Try to push only after your branch is "stable", especially when working with other teammates on the same feature. If your push is gonna break the build because it's still incomplete or you couldn't test it all on time, tell that to your co-workers so that you won't waste their time.

  • Again, possibly NEVER push commits with failing specs (especially for this project). Investigate the issue before committing & pushing the changes. Disable the test only as a last resort, if you really, really have to commit anyway. The build is automated and is run as soon as you push the changes. Having the build tested locally for passing before pushing the changes is not imperative but it's at least a polite but firm request to reduce error messages to the maintainers.

Keep in mind that, although most of the Rails stuff gets refreshed inside the Zeus/Spring context, when modifying certain start-up application files (config/*, lib/*, ...) a full cold-restart of the preloading server may be required anyway to reflect the changes. (This affects especially older versions of both Zeus & Spring.)

In this case, simply hit CTRL-C to halt Zeus (or spring stop for Spring, when started with spring server), followed by a zeus start (or spring server) afterwards.

The Guardfile stored on the repos is currently configured to use spring, but it can be easily modified for Zeus usage (like it was in legacy versions).

When using Guard with Spring, if you need to restart just Spring, exit Guard and re-run it.

Tests: writing & running

Wondering writing which tests and with what?

  • Prefer RSpec for unit, requests & generic back-end testing. Especially any Unit testing for any base object (Factories, Decorators, Strategies, Services, ...) should be as exhaustive as possible: the more closer you are to the business-logic the more regression-free you should get.

  • We use Cucumber for writing front-end & integration testing, running different device drivers (using Capybara) directly on the build pipeline, for all the feature specs in each supported device.

All the features in the suite can still be tested locally using the default desktop Capybara driver with a background running guard, but just one device will be tested (because this makes the long integration feature specs more tolerable on localhost). The remaining supported device screens will be tested once the build is locally green and can be pushed onto the Continuous Deployment pipeline.

Check out our dedicated WiKi page about in-depth principles and best-practice patterns for more details.

Manually testing with a specific physical device

In case you'll find yourself curious about manually testing the running application on a specific physical device which is not listed in the devices run by the CD Pipeline, avoid editing the default Capybara driver in the configuration files: you'll just need to temporarily allow connections from any device connected to your LAN or Wi-Fi.

To allow connections to a Rails server running locally:

  1. start the server binding to your LAN IP (i.e.: rails s -b 192.168.0.XX)

  2. make sure the default Rails port (3000) is accessible from the firewall configuration: sudo ufw status

  3. if the rule is missing, add it with

    $> sudo ufw allow from 192.168.0.0/24 to any port 3000 proto tcp

Routing the localhost:3000 port to your LAN IP is overkill (most of the times).


Coding & naming guidelines for most projects

Assuming familiarity with all of the above, the most common best-practices & the Rails naming conventions, we'll add here any additional notes as generic guidelines for the framework:

Filenames:

  • Use singular for models (models/user.rb) and factories (spec/factories/user.rb).
  • Use plural only for for migrations (db/migrate/20170926131744_create_users.rb) or controllers files (except in some obvious cases, like base_controller).

🚧

Variables and method names:

  • Prefer verbose and talkative names to cryptic ones.
  • Acronyms can be tolerated and may be useful sometimes, but only if the complete name is over 20 chars. (ex: mc_hypertension is surely more manageable than medical_condition_hypertension)

🚧

RDOC & Documentation in general:

TL'DR: From our experience it's better to write "excessive" documentation than to write none or scarce. It always easier to remove than to add: documentation written down can be revised, edited & improved; whatever'll be found as missing afterwards it won't be that easy to add.

Consider documenting what you do as much as an imperative to improve the quality of your work and your working life in the long term.

  • If you come across a complex process or issue, write a WiKi page about it, even terse, so that you may then enrich it step-by-step, coming along with your comprehension deepening of it.

  • Write a succinct message up front on each method in the code, even on private ones, especially if the goal of the method is not-so obvious.

  • Document each parameter and the resulting outcome whenever possible. For non-trivial formats, add some output examples too, or some kind of format specification.

  • Try not to be too repetitive: if something is needed or referenced multiple times, prefer copy-pasting a link from the Wiki inside the code comment or add another external reference.

  • Nevertheless and at the same time, usually for safety reasons, as the latinists would say: "repetita iuvant" ("repeating helps"). Meaning that, sometimes, it's better to restate even the obvious because it will aid the young interns working on your "great library code" (with apologies to the older and sensible senior developers which may become offended by that).

    This will shorten the onboarding process for whomever may take your place in the future or for any additional team members that may found themselves working on your code.

  • Don't take your mind processes fpr granted because what it seems so obvious today it will become inevitably foggy in months to come. Even for yourself.

  • If the above sounds somewhat oblivious... Close your eyes for at least 10 seconds and try to picture yourself in a handful of years. Take the following as an exercise: the more you'll do it, the easier it will get to know what to write.

    Don't read further but proceed with calm: really, relax up a bit, count up to ten and slow it down while picturing the future... (I MEAN IT. 😄)

    Have you really count up to ten? Make sure you do it.

    If you're having trouble relaxing, try saying "Six months passing by..." in your head for at least 2 or 3 times, with your eyes closed, imagining another period in your life in which that actually occurred. Try to experience "life progressing" and stuff like that.

    ...Are you there now? I hope so.

    ...Well...

    Imagine, for whatever reason it may be, that you really have to work again with the code you happen to wrote in this timeline. Just that single piece of code - not the whole directory of files. Is the code obvious? It is clear? Be honest and answer that to yourself. I know, you may not want to write anything at all, but... Just add any additional comment or note in the code so that your future older self (or anybody else in your place, possibly dumber) may follow your current reasoning as quickly and easily as possible.

    Just like that. 'Wasn't hard, right?

Additional generic coding principles:

  • Check out our dedicated WiKi page about in-depth principles and best-practice patterns for more details.

  • The "single-responsibility" principle should be one of your best friend: create objects that do just one thing and do that well. All custom objects should have dedicated specs.

  • Also, be accustomed to software patterns like "Factories", "Proxies" and "Strategy" or "Service Objects" as these will come in handy more often than you may think. Whenever you'll find yourself with a convoluted piece of Business Logic, that is probably a good place to start refactoring the code with one of these.

  • Prefer ViewComponents to reusable partials. ViewComponents are a first-class citizen in Rails 6.1+ and can be tested and previewed in isolation.

  • Use code linters (such as Rubocop) or any static analysis tool to improve the code quality all the time whenever possible.

  • Keep "cruft" production as low as possible and try to repay constantly the Technical Debt accumulated, because Technical Debt inflates rather quickly and may affect the exact features for what you're accumulating it first. (If you don't know what Techincal Debt or Cruft is, just check that link out.)

    In other words, if you find yourself using something (a library, a gem, a tool) because "magically it just works" and it develops some kind of binding or dependency with the rest of the project, plan ahead some time to clean-up, deepen & improve your understanding of it, so that you may be able to explain its inner workings even to your team mates.

    As said above in "documenting your work", try to add a WiKi page about it, so that others may contribute to it. By sharing knowledge you'll keep the overall "cruft" low.


Notes on Developer effectiveness:

Take the time to read the following great article 🔗 about maximizing developer effectiveness.

This is just an extracted example:

A developer's day in a highly effective environment

The developer:

  • Checks the team project management tool and then attends standup where she is clear about what she has to work on.
  • Notes that the development environment has been automatically updated with libraries matching development and production, and the CI/CD pipelines are green.
  • Pulls down the latest code, makes an incremental code change that is quickly validated by deploying to a local environment and by running unit tests.
  • Depends on another team’s business capabilities for her feature. She is able to find documentation and the API spec through a developer portal. She still has some queries, so she jumps into the team’s Slack room and quickly gets some help from another developer who is doing support.
  • Focuses on her task for a few hours without any interruptions.
  • Takes a break, gets coffee, takes a walk, plays some ping pong with colleagues.
  • Commits the code change, which then passes through a number of automated checks before being deployed to production.
  • Releases the change gradually to users in production, while monitoring business and operational metrics.

The developer is able to make incremental progress in a day, and goes home happy.

A developer's day in a low effective environment

The developer:

  • Starts the day having to deal immediately with a number of alerts for problems in production.
  • Checks a number of logging and monitoring systems to find the error report as there are no aggregated logs across systems.
  • Works with operations on the phone and determines that the alerts are false positives.
  • Has to wait for a response from architecture, security and governance groups for a previous feature she had completed.
  • Has a day broken up with many meetings, many of which are status meetings
  • Notes that a previous feature has been approved by reviewers, she moves it into another branch that kicks off a long nightly E2E test suite that is almost always red, managed by a siloed QA team.
  • Depends on another team's API, but she cannot find current documentation. So instead she talks to a project manager on the other team, trying to get a query. The ticket to find an answer will take a few days, so this is blocking her current task.

We could go on. But ultimately the developer doesn’t achieve much, leaves frustrated and unmotivated.

If you relate to this even a bit, you'll get the point.

Clone this wiki locally