Notes of Best Practices for writing Cypress tests

Cypress is a nice tool for end-to-end tests and it has good documentation also for Best Practices including "Cypress Best Practices" talk by Brian Mann at Assert(JS) 2018. Here are my notes from the talk combined with the Cypress documentation. This article assumes you know and have Cypress running.

In short:

  • Set state programmatically, don't use the UI to build up state.
  • Write specs in isolation, avoid coupling.
  • Don't limit yourself trying to act like a user.
  • Tests should always be able to be run independently and still pass.
  • Only test what you control.
  • Use data-* attributes to provide context to your selectors.
  • Clean up state before tests run (not after).

Organizing tests

- Don't use page objects to share UI knowledge
+ Write specs in isolation, avoid coupling

"Writing and Organizing tests" documentation just tells you the basics how you should organize your tests. You should organize tests by pages and by components as you should test components individually if possible. So the folder structure for tests might look like.

├ articles
├── article_details_spec.js
├── article_new_spec.js
├── article_list_spec.js
├ author
├── author_details_spec.js
├ shared
├── header_spec.js
├ user
├── login_spec.js
├── register_spec.js
└── settings_spec.js

Selecting Elements

- Dont' use highly brittle selectors that are subject to change.
+ Use data-* attributes to provide context to your selectors and insulate them from CSS or JS changes.

Add data-* attributes to make it easier to target elements.

For example:

<button id="main" class="btn btn-large" name="submit"
  role="button" data-cy="submit">Submit</button>

Writing Tests

- Don't couple multiple tests together.
+ Tests should always be able to be run independently and still pass.

Best practice when writing tests on Cypress is to iterate on a single one at a time, i.a.

describe('/login', () => {

  beforeEach() => {
    // Wipe out state from the previous tests
    cy.visit('/#/login')
  }

  it('requires email', () =>
    cy.get('form').contains('Sign in').click()
    cy.get('.error-messages')
    .should('contain', 'email can\'t be blank')
  })

  it('requires password', () => {
    cy.get('[data-test=email]').type('joe@example.com{enter}')
    cy.get('.error-messages')
    .should('contain', 'password can\'t be blank')
  })

  it('navigates to #/ on successful login', () => {
    cy.get('[data-test=email]').type('joe@example.com')
    cy.get('[data-test=password]').type('joe{enter}')
    cy.hash().should('eq', '#/')
  })

})

Note that we don't add assertions about the home page because we're on the login spec, that's not our responsibility. We'll leave that for the home page which is the article spec.

Controlling State

"abstraction, reusability and decoupling"

- Don't use the UI to build up state
+ Set state directly / programmatically

Now you have the login spec done and it's the cornerstone for every single test you will do. So how do you use it in e.g. settings spec? For not to copy & paste login steps to each of your tests and duplicating code you could use custom command: cy.login(). But using custom command for login fails at testing in isolation, adds 0% more confidence and accounts for 75% of the test duration. You need to log in without using the UI. And to do that depends of how your app works. For example you can check for JWT token in the App and in Cypress make a silent (HTTP) request.

So your custom login command becomes:

Cypress.Commands.add('login', () => {
  cy.request({
    method: 'POST',
    url: 'http://localhost:3000/api/users/login',
    body: {
      user: {
        email: 'joe@example.com',
        password: 'joe',
      }
    }
  })
  .then((resp) => {
    window.localStorage.setItem('jwt', resp.body.user.token)
  })
})

Setting state programmatically isn't always as easy as making requests to endpoint. You might need to manually dispatch e.g. Vue actions to set desired values for the application state in the store. Cypress documentation has good example of how you can test Vue web applications with Vuex data store & REST backend.

Visiting external sites

- Don't try to visit or interact with sites or servers you do not control.
+ Only test what you control.

Try to avoid requiring a 3rd party server. When necessary, always use cy.request() to talk to 3rd party servers via their APIs like testing log in when your app uses another provider via OAuth. Or you could try stub out the OAuth provider. Cypress has recipes for different approaches.

Add multiple assertions

- Don't create "tiny" tests with a single assertion and acting like you’re writing unit tests.
+ Add multiple assertions and don’t worry about it

Cypress runs a series of async lifecycle events that reset state between tests. Resetting tests is much slower than adding more assertions.

it('validates and formats first name', function () {
    cy.get('#first')
      .type('johnny')
      .should('have.attr', 'data-validation', 'required')
      .and('have.class', 'active')
      .and('have.value', 'Johnny')
  })

Clean up state before tests run

- Don't use after or afterEach hooks to clean up state.
+ Clean up state before tests run.

When your tests end - you are left with your working application at the exact point where your test finished. If you remove your application's state after each test, then you lose the ability to use your application in this mode or debug your application or write a partial tests.

Unnecessary Waiting

- Don't wait for arbitrary time periods using cy.wait(Number).
+ Use route aliases or assertions to guard Cypress from proceeding until an explicit condition is met.

For example waiting explicitly for an aliased route:

cy.server()
cy.route('GET', /users/, [{ 'name': 'Maggy' }, { 'name': 'Joan' }]).as('getUsers')
cy.get('#fetch').click()
cy.wait('@getUsers')     // <--- wait explicitly for this route to finish
cy.get('table tr').should('have.length', 2)

No constraints

You've native access to everything so don't limit yourself trying to act like a user. You can e.g.

  • Control Time: cy.clock(), e.g. control how your app responds to system time, force set timeouts and set intervals to fire when you want them to.
  • Stub Objects: cy.stub(), force callbacks to fire, assert things are called with right arguments.
  • Modify Stores: cy.window(), e.g. dispatch events, like logout.

Set global baseUrl

+ Set a baseUrl in your configuration file.

Adding a baseUrl in your configuration allows you to omit passing the baseUrl to commands like cy.visit() and cy.request().

Without baseUrl set, Cypress loads main window in localhost + random port. As soon as it encounters a cy.visit(), it then switches to the url of the main window to the url specified in your visit. This can result in a ‘flash’ or ‘reload’ when your tests first start. By setting the baseUrl, you can avoid this reload altogether.

Assertions should be obvious

"A good practice is to force an assertion to fail and see if the error message and the output is enough to know why. It is easiest to put a .only on the it block you're evaluating. This way the application will stop where a screenshot is normally taken and you're left to debug as if you were debugging a real failure. Thinking about the failure case will help the person who has to work on a failing test." (Best practices for maintainable tests)

<code>
it.only('check for tab descendants', () => {
  cy
    .get('body')
    .should('have.descendants', '[data-testid=Tab]') // expected '' to have descendants '[data-testid=Tab]'
    .find('[data-testid=Tab]')
    .should('have.length', 2) // expected '[ <div[data-testid=tab]>, 4 more... ]' to have a length of 2 but got 5
});
</code>

Explore the environment

You can pause the test execution by using debugger keyword. Make sure the DevTools are open.

it('bar', function () {
   debugger
   // explore "this" context
 })

Running in CI

If you're running in Cypress in CI and need to start and stop your web server there's recipes showing you that.

Try the start-server-and-test module. It's good to note that when using e2e-cypress plugin for vue-cli it starts the app automatically for Cypress.

If your videos taken during cypress run freeze when running on CI then increase the CPU resources, see: #4722

Adjust the compression level on cypress.json to minimal with "videoCompression": 0 or disable it with "videoCompression": false. Disable recording with "video": false.

Record success and failure videos

Cypress captures videos from test runs and whenever a test fails you can watch the failure video side by side with the video from the last successful test run. The differences in the subject under test are quickly obvious as Bahtumov's tips suggests.

If you're using e.g. GitLab CI you can configure it to keep artifacts from failed test runs for 1 week, while keeping videos from successful test runs only for a 3 days.

artifacts:
    when: on_failure
    expire_in: '1 week'
    untracked: true
    paths:
      - cypress/videos
      - cypress/screenshots
  artifacts:
    when: on_success
    expire_in: '3 days'
    untracked: true
    paths:
      - cypress/screenshots

Helpful practices

Disable ServiceWorker

ServiceWorkers are great but they can really affect your end-to-end tests by introducing caching and coupling tests. If you want to disable the service worker caching you need to remove or delete navigator.serviceWorker when visiting the page with cy.visit.

it('disable serviceWorker', function () {
  cy.visit('index.html', {
    onBeforeLoad (win) {
      delete win.navigator.__proto__.serviceWorker
    }
  })
})

Note: once deleted, the SW stays deleted in the window, even if the application navigates to another URL.

Get command log on failure

In the headless CI mode, you can get a JSON file for each failed test with the log of all commands. All you need is cypress-failed-log project and include it from your cypress/support/index.js file.

Conditional logic

Sometimes you might need to interact with a page element that does not always exist. For example there might a modal dialog the first time you use the website. You want to close the modal dialog. But the modal is not shown the second time around and the above code will fail.

In order to check if an element exists without asserting it, use the proxied jQuery function Cypress.$:

const $el = Cypress.$('.greeting')
if ($el.length) {
  cy.log('Closing greeting')
  cy.get('.greeting')
    .contains('Close')
    .click()
}
cy.get('.greeting')
  .should('not.be.visible')

Summary

- Don't use the UI to build up state
+ Set state directly / programmatically

- Don't use page objects to share UI knowledge
+ Write specs in isolation, avoid coupling

- Don't limit yourself trying to act like a user
+ You have native access to everything

- Don't couple multiple tests together.
+ Tests should always be able to be run independently and still pass.

- Don't try to visit or interact with sites or servers you do not control.
+ Only test what you control.

- Dont' use highly brittle selectors that are subject to change.
+ Use data-* attributes to provide context to your selectors

- Don't create tests with a single assertion
+ Add multiple assertions and don’t worry about it

- Don't use after or afterEach hooks to clean up state.
+ Clean up state before tests run.

+ Set a baseUrl in your configuration file.

More to read

Use cypress-testing-library which encourage good testing practices through simple and complete custom Cypress commands and utilities.

Set up intelligent code completion for Cypress commands and assertions by adding a triple-slash directive to the head of your JavaScript or TypeScript testing spec file. This will turn the IntelliSense on a per file basis.

/// <reference types="Cypress" />

Read What I’ve Learned Using Cypress.io for the Past Three Weeks if you need a temporary workaround for iframes and testing file uploads as for now Cypress does not natively support those.

And of course Gleb Bahmutov's blog is useful resource for practical things like Tips and tricks post.

Monthly notes 46

December is full Christmas carrols and hassle before holidays. So, take a short break and learn to master Kubernetes, become better human and developer and make remote (working) a success. Also think about privacy. Good reading and happy holidays!

Issue 46, 17.12.2019

Cloud

Mastering the KUBECONFIG file
Good tips like Auto-$KUBECONFIG based on directory with direnv; Know which context you’re pointing at with kube-ps1; Save GKE contexts to separate files. (from @walokra)

Tutorial: Debug Your Kubernetes Apps (youtube)
Debug your Kubernetes apps tutorial from KubeCon. Slides: https://aws-samples.github.io/debug-k8s-apps/#/, code: https://github.com/aws-samples/debug-k8s-apps. Covers cluster design, networking, kubectl, pods, lb & ingress, monitoring, resource reservation and stateful sets. (from @ArunGupta)

JavaScript

20 ways to become a better Node.js developer in 2020
"20 skills, technologies and considerations on choosing between them. Picking the right tools became one of our greatest challenges — the Node.js ecosystem has matured and present attractive options in almost every field. Vanilla or TypeScript? Ava, Mocha or Jest? Express, Fastify or Koa? or maybe Nest?"

Learning

Things You Should Read To Become A Better Human & Developer
"As developers, we are creators of systems and worlds. However, to be effective at our jobs, we need to understand these systems and worlds we’re creating. When we read, we expand the borders that define our domain of knowledge."

Don’t Learn to Code — Learn to Automate
"avoid thinking of writing code as the goal and learn to solve problems."

A Guide to Distributed Teams
How thoughtful systems (and lots of emoji) make for happy, efficient teams—whether your desks are distributed across floors, cities, or continents. Hacker News comments

How to Make Remote a Success
"It's all about sharing and communicating". E.g. Write down everything: knowledge base to blog posts, make weekly notes; Make everyone feel connected: smarter meetings, daily check-ins/check-outs. Hacker News comments

Privacy

You’re Tracked Everywhere You Go Online. Use This Guide to Fight Back
Advertisers are tracking and monitoring your behavior almost everywhere you go online. Here's how to (mostly) stop it. (from @TimHerrera)

Privolta Consent Study: Google
Great example how to quantify the degree to which 'dark patterns' dominate privacy consent interactions online. (from @ashk4n)

Tools

Falco
Falco is an automatic, easy-to-use Web Performance auditing tool. Open Source WebPageTest runner which helps you monitor, analyze, and optimize your websites. (from @PHacks)

Fx
Command-line tool and terminal JSON viewer. "If you’ve got some files full of JSON that you want to process, Fx will slice and dice it however you want, including using JavaScript one-liners to add a bit of logic to the process." (from DB Weekly #284)

Monthly notes 45

Snow is covering the ground and hibernation period starts? Or more time inside reading and learning new things? Here's monthly notes for Octorber.

Issue 45, 30.10.2019

Software Development

What qualities make up a 1x engineer?
I can relate to this.

My favourite Git commit
Good example how git commit messages should be done especially if the change is ambiguous. Doing explanatory commits need extra effort than just “Fixed it” but it pays out later. (from @walokra)

DevOps

A Practical Framework for DevSecOps
Nice overview to key #DevSecOps domains and activities. “With a limited budget start with Monitoring and Responding. Then focus on how to prevent vulnerabilities from being introduced in the first place.” (from @walokra)

Docker for Pentesters
Docker has completely changed my workflow, and I wrote up 10 examples and scripts for how pentesters can leverage Docker to speed up testing. Lmk how you use Docker - this could be a series! (from @walokra)

iOS

Announcing my Shortcuts Library, featuring 150 Siri Shortcuts to use with iOS 13
With iOS 13, Shortcuts is installed by default on every device – hundreds of millions of people will inevitably use this app now. And you can control them with Siri.

Technology

The secret life of GPS trackers
"We decided to take a look at several child (GPS) trackers available on Amazon, eBay, and Alibaba to see how they stood up to our scrutiny."

Something different

Dumbass Home 2.0
Excellent overview to “Smart” home and available solutions. "S in IoT stands for Security” so use separate WiFi, Zigbee, hub with Raspberry Pi, Raspbee & Home Assistant (or Hue/SmartThings), gadgets from Trådfri, Xiaomi (~), Philips & Osram with discount. (from @walokra)

Automate validating code changes with Git hooks

What could be more annoying than committing code changes to repository and noticing afterwards that formatting isn't right or tests are failing? Your automated tests on Continuous Integration shows rain clouds and you need to get back to the code and fix minor issues with extra commits polluting the git history? Fortunately with small enhancements to your development workflow you can automatically prevent all the hassle and check your changes before committing them. The answer is to use Git hooks for example on pre-commit for running linters and tests.

Git Hooks

Git hooks are scripts that Git executes before or after events such as: commit, push, and receive. They're a built-in feature and run locally. Hook scripts are only limited by a developer's imagination. Some example hook scripts include:

  • pre-commit: Check the commit for linting errors.
  • pre-receive: Enforce project coding standards.
  • post-commit: Email team members of a new commit.
  • post-receive: Push the code to production.

Every Git repository has a .git/hooks folder with a script for each hook you can bind to. You're free to change or update these scripts as necessary, and Git will execute them when those events occur.

Git hooks can greatly increase your productivity as a developer as you can automate tasks and ensure that your code is ready for commit or pushing to remote repository.

For more reading about Git hooks you can check missing Git hooks documentation, read the basics and check tutorial how to use Git hooks on local Git clients and Git servers.

Pre-commit

One productive way to use Git hooks is pre-commit framework for managing and maintaining multi-language pre-commit hooks. Read tips for using a pre-commit hook.

Pre-commit is nice for example running linters to ensure that your changes conform to coding standards. All you need is to install pre-commit and then add hooks.

Installing pre-commit, ktlint and pre-commit-hook on MacOS with Homebrew:

$ brew install pre-commit
$ brew install ktlint
$ ktlint --install-git-pre-commit-hook

For example the pre-commit hook to run ktlint with auto-correct option looks like the following in projects .git/hooks/pre-commit. The "export PATH=/usr/local/bin:$PATH" is for SourceTree to find git on MacOS.

#!/bin/sh
export PATH=/usr/local/bin:$PATH
# https://github.com/shyiko/ktlint pre-commit hook
git diff --name-only --cached --relative | grep '\.kt[s"]\?$' | xargs ktlint -F --relative .
if [ $? -ne 0 ]; then exit 1; else git add .; fi

The main disadvantage is using pre-commit and local git hooks is that hooks are kept within .git directory and it never comes to the remote repository. Each contributor will have to install them manually in his local repository which may be overlooked.

Maven projects

Githook Maven plugin deals with the problem of providing hook configuration to the repository and automates their installation. It binds to Maven projects build process and configures and installs local git hooks.

It keeps a mapping between the hook name and the script by creating a respective file in .git/hooks for each hook containing given script in Maven project's initial lifecycle phase. It's good to notice that the plugin rewrites hooks.

Usage Example:

<build>
    <plugins>
	<plugin>
	    <groupId>org.sandbox</groupId>
	    <artifactId>githook-maven-plugin</artifactId>
	    <version>1.0.0</version>
	    <executions>
	        <execution>
	            <goals>
	                <goal>install</goal>
	            </goals>
	            <configuration>
	                <hooks>
	                    <pre-commit>
	                         echo running validation build
	                         exec mvn clean install
	                    </pre-commit>
	                </hooks>
	            </configuration>
	        </execution>
	    </executions>
	</plugin>
    </plugins>
</build>

Git hooks for Node.js projects

On Node.js projects you can define scripts in package.json and run them with npm which enables an another approach to running Git hooks.

🐶 Husky is Git hooks made easy for Node.js projects. It keeps existing user hooks, supports GUI Git clients and all Git hooks.

Installing Husky is like any other npm library

npm install husky --save-dev

The following configuration on your package.json runs lint (e.g. eslint with --fix) command when you try to commit and runs lint and tests (e.g. mocha, jest) when you try to push to remote repository.

"husky": {
   "hooks": {
     "pre-commit": "npm run lint",
     "pre-push": "npm run lint && npm run test"
   }
}

Another useful tool is lint-staged which utilizes husky and runs linters against staged git files.

Summary

Make your development workflow easier by automating all the things. Check your changes before committing them with pre-commit, husky or Githook Maven plugin. You get better code and commit quality for free and your team is happier.

This article was originally published at 15.7.2019 on Gofore's blog.

Monthly notes 44

Summer holidays are over and it's time to get back to work and monthly notes. I spent almost whole August enjoying nature, mountain biking, hiking and coaching young mountainbikers. Less computers, more relaxing. This month's notes are about writing great Docker images, validate code using git hooks, log management, story about npm registry, working remotely and effective Kotlin. Happy reading.

Issue 44, 6.9.2019

Microservices

How to write great Docker container images
It's easy with these great tips and examples. I would add that use small base image like Alpine Linux if possible. (from @walokra)

Kubernetes: A Detailed Example of Deployment of a Stateful Application
Article goes through the overview to Kubernetes by covering "What are the design principles and architecture of Kubernetes?" and "How to use Kubernetes, and a simple example." (from @java)

Software Development

Automate validating code changes with Git hooks
What could be more annoying than committing code changes and noticing afterwards that the formatting isn’t right or tests are failing? Read these tips how automate validating code changes with git hooks and make your flow smooth.

Fast log management for your apps
Nicolas Frankel talked at Berlin Buzzwords about logging. Good overview to the issue. TL;DR; no computation to logs, filesystem matters, asynchronous vs. reliability, no expensive meta-data, schema on write, send JSON.

Use morning hours for open source and improving

You're better at your work when you're improving your technical craft.

JavaScript

Story of money and ownership and control
"the economics of open source [in JavaScript, Node.js and npm]". Important point of views to problems with (privately controlled) [npm] package registry. (from @walokra)

Team work

11 Best Practices for Working Remotely
Good tips for working remotely. The biggest hurdles are communication, social opportunities and loneliness and isolation. "With consistent effort, you can overcome the challenges of remote work and create a healthy, happy, productive environment for yourself and for your team." (from @dunjardl)

If you ever have to lead a remote dev team…
The Remote Workflow:simple, transparent, predictable, frictionless. (from @ThePracticalDev)

Books

Effective Kotlin beta release
Adding this to my reading list! "First official version of Effective Kotlin is finally in distribution (as an ebook)". Having read Effective Java this book is totally worth it.

Something different

Watch 14 minutes of new Cyberpunk 2077 gameplay footage
A new look at different gameplay styles for the upcoming open-world RPG.

Monthly notes 43

Issue 43, 25.7.2019

Microservices

How to write great container images
Article shows the principles of what writes consider "Dockerfile best practices", and simultaneously walks through them with a real example. I would add that use small base image like Alpine Linux if possible.

Micro Frontends
The article describes breaking up frontend monoliths into many smaller, more manageable pieces, and how this architecture can increase the effectiveness and efficiency of teams working on frontend code. As well as talking about the various benefits and costs, it covers some of the implementation options that are available, and dives deep into a full example application that demonstrates the technique.

Performance

Performance Analysis Methodology
Informative presentation of Performance Analysis Methodology by Brendan Gregg at LISA '12. Focus on the USE method which all staff can use for identifying common bottlenecks and errors. Check for: Utilization, Saturation, Errors. (from walokra)

Fast log management for your apps
You've migrated your application to Reactive Microservices to get the last ounce of performance from your servers. But what about logs? Logs can be one of the few roadblocks on the road to ultimate performance. Nicolas Frankel shows in his talk at Berlin Buzzwords 2019 some insider tips and tricks taken from our experience put you on the track toward fast(er) log management.

JavaScript

single-spa
A javascript framework for front-end microservices.

Node.js Memory Management in Container Environments
Best practices for managing memory in container-based Node apps. (from JavaScript Daily)

CTU JavaScript Guide
Opinionated guide to ground rules for an application’s JavaScript code, such that it’s highly readable and consistent across different developers on a team. The focus is put on quality and coherence across the different pieces of your application.

Security

Nginx Admin's Handbook
nginx is a powerful web server but with great power comes great responsibility (to configure it for security and performance). "Nginx Admin's Handbook" is a good collection of rules, helpers, notes and papers, best practices and recommendations to achieve it. (from walokra)

GOTCHA: Taking phishing to a whole new level
Without X-FRAME-OPTIONS you can build a  UI redressing attack that allows attackers to extract valuable information from API endpoints. tl; dr; extract chars with CSS, add captcha form, scramble chars, get user to fill in the password-captcha.

Staying Safe on GitHub: The Ultimate GitHub Security Tools Roundup
Nice overview to #security tools for #GitHub repositories. GitHub Security Alerts is provided by default, additionally use one of these: Snyk, WhiteSource Bolt, Sonatype DepShield. (from walokra)

Something different

It's Summer and there's plenty of Natural Parks in Finland. Go and create your Summer adventure in the wilderness. From Southern Archipelago to Northern Fells: Pallas-Yllästunturi, UKK, Pyhä-Luosto, Koli, Nuuksio.

Ignoring files and folders in Subversion with propset

Before committing code to the Subversion repository we always set the svn:ignore property on the directory to prevent some files and directories to be checked in. You would usually want to exclude the IDE project files and the target/ directory.

It's useful to put all the ignored files and directories into a file: .svnignore. Your .svnignore could look like:

*.iml
target/*

Put the .svnignore file in the project folder and commit it to your repository so the ignored files are shared between committers.

Now reference the file with the -F option:
$ svn propset svn:ignore -F .svnignore.

Of course I hope everyone has by now moved to git and uses .gitignore for this same purpose.

Monthly notes 42

Midsummer is couple of days away and it's time to take a short break from work and enjoy the Summer nights and nature. And if you have time here is a short list of articles to read and videos from React Finland 2019 conference to watch.

Issue 42, 20.6.2019

Software Development

Consulting or con-$ulting
A theory on how Hertz’s inexperience in buying software — combined with Accenture’s incompetence to deliver it — flushed $32M+ down the drain. "The lack of transparency and technical expertise combined with the lack of ownership/responsibility was ultimately the reason why Hertz managed to blow tens of millions USD, instead of just a couple." Lessons learned: "If you are buying software for tens of millions, you must have an in-house technical expert as part of the software development process".

Dont' stop writing code comments
"You should write comments which matter." @nicolas_frankel)

Improve your technical craft
More knowledge helps you to do your work (more efficiently and in less time). In knowledge work time spent at the office doesn't equal productivity or job well done.

Or rather than 1hr per day, batch it all on e.g. Fridays when things are slow.

React Finland 2019 presentations
29 videos from React Finland 2019 conference. Some picks: Automation and Exploratory testing, More Accessible React Apps, guide to building your design system infrastructure.

Databases

Be careful with CTE in PostgreSQL
PostgreSQL doesn't inline common table expressions, WITH clause, it materializes it and thus is unable to utilize the index => expensive. Good to know if you're used to Oracle which doesn't materialize CTEs by default. (from walokra)

UX

Can't Unsee
"The devil is in the details". A game where your attention to details earns you a lot of coins. Fun game which teaches you some UX rules and attention to details. With 5780 coins I'm a beginner :/ (or need glasses :))

It feels fine on my phone
"You literally can't afford desktop or iphone levels of JS if you're trying to make good web experiences for anyone but the world's richest users, and that likely means re-evaluating your toolchain."

"Gap between "what I get when I trade in my phone every 2 years" and the true low-end is now a gaping chasm, and even 5 years ago, that wasn't true."

Something different

Arofly Link brings power meters under $200 using a tire pressure monitor
Forget power meters in cranks and pedals, here's Arofly Link. "Unlike most power meters that measure the actual force being put into your bike’s drivetrain between the pedals and the rear hub, Arofly takes it one step further apparently measuring power where the rubber meets the road."

Monthly Notes 41

Issue 41, 31.5.2019

Software development

Gitmoji
If not considering the issue on Bamboo with this (thread), Using Emojis in Git commit messages is a nice idea. There's even cool emoji guide for your commit messages. Going to take this into use 😊 (from walokra)

Happy Friday, Don't push to production?
Good thread of how you should treat your deploys to production. You should deploy often and have good CI/CD practices but the overall question isn't black or white. "Nothing goes wrong until it does, and then you'd want your people available."  "If you're scared of pushing to production on Fridays, I recommend reassigning all your developer cycles off of feature development and onto your CI/CD process and observability tooling for as long as it takes to ✨fix that✨." (from walokra)

Sleep quality and stress level matter and after 24 hours awake
"Your sleep quality and stress level matter far, far more than the languages you use or the practices you follow. Nothing else comes close". Good notes of why sleeping and rest matters (thread) 😴 There's always more work to do, take care of yourself first! (from walokra)

Software architecture

Why software architects fail – and what to do about it
Looks at some of the most common pitfalls that ensure you’ll come up with a disaster, and discusses how they can be avoided.

Cloud

High" levels of ☁️ spending at Lyft
Continuation on the Internet-discussion whether Lyft’s spending on AWS is too high and they could do better on-premises.

Frontend

TSLint in 2019
"Once we consider ESLint feature-complete w.r.t. TSLint, we will deprecate TSLint and help users migrate to ESLint"

User Experience

Printable A3 posters for Laws of UX
(from JonYablonski)

Something different

"Work starts from problems and learning starts from questions. Work is creating value and learning is creating knowledge. Both work and learning require the same things: interaction and engagement." (from EskoKilpi)

Best Practices for Version Control in 8 steps

Using version control is an essential part of modern software development and using it efficiently should be part of every developer's tool kit. Knowing the basic rules makes it even more useful. Here are some best practices that help you on your way.

tl; dr;

  1. Commit logical changesets (atomic commits)
  2. Commit Early, Commit Often
  3. Write Reasonable Commit Messages
  4. Don't Commit Generated Sources
  5. Don't Commit Half-Done Work
  6. Test Before You Commit
  7. Use Branches
  8. Agree on a Workflow
Simplified Git Flow
Simplified Git Flow (source: buildazure)

Commit logical changesets (atomic commits)

A commit should be a wrapper for related changes. Make sure your change reflects a single purpose: the fixing of a specific bug, the addition of a new feature, or some particular task. Small commits make it easier for other developers to understand the changes and roll them back if something went wrong.

Your commit will create a new revision number which can forever be used as a "name" for the change. You can mention this revision number in bug databases, or use it as an argument to merge should you want to undo the change or port it to another branch. Git makes it easy to create very granular commits.

So if you do many changes to multiple logical components at the same time, commit them in separate parts. That way it's easier to follow changes and their history. So working with features A, B and C and fixing bugs 1, 2 and 3 should make at least 6 commits.

Commit Early , Commit Often

It is recommended to commit code to version control often which keeps your commits small and, again, helps you commit only related changes. It also allows you to share your code more frequently with others.

It's easier for everyone to integrate changes regularly and avoid having merge conflicts. Having few large commits and sharing them rarely, in contrast, makes it hard to solve conflicts.

"If the code isn't checked into source control, it doesn't exist."

Coding Horror

Write Reasonable Commit Messages

Always write some reasonable comment on your commit. It should be short and descriptive and tell what was changed and why.

Begin your message with a short summary of your changes (up to 50 characters as a guideline). Separate it from the following body by including a blank line.

It is also useful to add some prefix to your message like Fix or Add, depending on what kind of changes you did. Use the imperative, present tense ("change", not "changed" or "changes") to be consistent with generated messages from commands like git merge.

If fixing some bug or making some feature and it has a JIRA ticket, add the ticket identifier as a prefix.

For example: "Fix a few bugs in the interface. Added an ID field. Removed a couple unnecessary functions. Refactored the context check." or "Fix bad allocations in image processing routines".

Not like this: "Fixed some bugs."

The body of your message should provide detailed answers to the following questions: What was the motivation for the change? How does it differ from the previous implementation?

"If the changes you made are not important enough to comment on, they probably are not worth committing either."

loop label

Don’t Commit Generated Sources

Don't commit files which are generated dynamically or which are user dependent. Like target folder or IDEA's .iml files or Eclipse's .settings and .project files. They change depending what the user likes and don't relate to project's code.

Also project's binary files and Javadocs are files that don't belong to version control.

Don't Commit Half-Done Work

You should only commit code when it's completed. Split the feature's implementation into logical chunks and remember to commit early and often. Use branches or consider using Git's Stash feature if you need a clean working copy (to check out a branch, pull in changes, etc.).

On the other hand you should never leave the office without commiting your changes.

"It's better to have a broken build in your working repository than a working build on your broken hard drive."

loop label

Test Before You Commit

You should only commit code which is tested and passes tests. And this includes code formatting with linters. Write tests and run tests to make sure the feature or bug fix really is completed and has no side effects (as far as one can tell).

Having your code tested is even more important when it comes to pushing / sharing your code with others.

Use Branches

Branching is one of Git's most powerful features – and this is not by accident: quick and easy branching was a central requirement from day one. Branches are the perfect tool to help you avoid mixing up different lines of development.

You should use branches extensively in your development workflows: for new features, bug fixes and ideas.

Agree on a Workflow

Git lets you pick from a lot of different workflows: long-running branches, topic branches, merge or rebase, git-flow.

Which one you choose depends on a couple of factors: your project, your overall development and deployment workflows and (maybe most importantly) on your and your teammates' personal preferences. However you choose to work, just make sure to agree on a common workflow that everyone follows.

Atlassian has done good article of comparing workflows to suit your needs and covers centralized, feature Branch, gitflow and forking workflows.

Summary

Using version control is usually and fortunately an acknowledged best practice and part of software development. By using even couple of the above practices makes working with the code much more pleasant. Adopting at least "Commit logical changesets" and "Reasonable Commit Messages" helps a lot.