Best practices for front-end teams: delivering fast and high quality modern apps at scale

By Nick Ribal, March 2019

Nick Ribal

image/svg+xml

Front-end & web consultant, architect, engineer, mentor

Family man, digital nomad, opinionated guy

My background

  • Over 10 years of web development, mostly front-end

  • ​Variety of
    • Targets: web, mobile, responsive, APIs
    • Products: websites, apps, widgets
    • Companies: small to big start-ups, large and corporate giants
    • Company styles: product, project, consulting
    • Technologies: each client/company is different, in BE and FE
    • Work formats: employee, team leader, tech lead, consultant, architect, on-site and remote

I've seen some 💩

Last few years

  1. ​Technological (easy)

    • Architect, choose technologies

    • Lay a solid foundation, which hopefully won't need to be thrown away and rewritten

  2. Cultural (hard)

    • Improve individual and organizational development speed and quality

    • Explore the complex interactions between code, people, culture and organizations using tooling, rules and conventions

I lead teams in two dimensions:

  • Senior developers

  • Team leaders

Target audience

Titles are dumb, so really

  • Tech leads

  • Architects

  • Anyone who cares and thinks about how people, code and organizations interact

You should know/care about

  • Pull Requests (PR)

  • Code reviews (CR)

  • Maintainability

  • Development speed

  • Mindset and guiding principles

  • Cultivating team & organizational culture

  • Tools and methodology

  • Soon to come: detailed blog post

Topics we'll cover

This isn't a very DRY topic

A lot of it is obvious

Sometimes, stating the obvious is necessary

Mindset and guiding principles

  • Avoid:

    • monolithic

    • opinionated

    • hard to replace parts

Do one thing, do it well. Compose.

  • Don't reinvent the wheel:

    • You can probably reuse 90% of others' work

    • Do your research

    • Only write glue code and your unique core product

    • Roll your own as last resort

  • Prefer:

    • small

    • single purpose

    • modular

Knowing the building blocks allows:

  • Meaningfully understand abstractions
  • Manage their trade-offs (instead of blindly following hype)

Sad example:

Not knowing CSS/HTML beyond a very basic level results in problems which shouldn't exist:

"div soup", bad a11y, CSS specificity/z-index wars

Learn the basics

  • Don't adopt everything new and shiny
  • See whether something gains real world traction
  • Give new tech time to mature
  • Have it battle tested and polished at someone else's expense

Examples:

  • CSS-in-JS took YEARS to mature from a good concept to a great implementation
  • Web components/Polymer are still struggling...

Avoid hype

  • Most people do something just because everyone else does the same
  • Don't do that. Use tech which solves YOUR problem

Popularity !== quality

Adopting and relying on something bad, will cost you a lot, for a very long time.

A bad abstraction is worse than no abstraction

I actively avoided Angular1 and Redux, even when absolutely everyone was using them.

You think something sucks? Avoid it!

Years later, many acknowledged the same problems I had identified years ago.

Follow your gut instinct

When you're wrong: "better late than never"

  • business domain
  • constraints

Be opinionated!

Always re-evaluate your opinions

It is OK to make mistakes

Never forget your:

  • core product
  • priorities

Do not ignore them and change your mind

as long as you

  • What's bad today may mature tomorrow
  • What's good today
    • may become unmaintained
    • will be replaced by something better
  • Apply critical thinking and constantly revisit your decisions

Examples of my own mistakes:

Don't get attached

  • TypeScript
  • eslint
  • stylelint
  • Spell checker

Set standards and enforce via static analysis tools

  • Dependency graph analysis
  • editorconfig
  • And more...

But don't limit or dictate what editor/IDE to use

  • Use prettier: focus on business, not whitespace
  • Linters: find possible bugs
  • Have tests: they make refactoring easy and catch mistakes early

Also: reduce cognitive load

Context switching is expensive

A short feedback loop shows mistakes live, in real-time, as you type is crucial.

But how?

  • Policing: annoying, never works
  • Following a protocol: people are too busy
  • Requiring everyone to use YOUR favorite IDE
  • "fixing the world" 1M LoC commit

Have you tried these?

Doesn't work. But this will:

  • "Just works", regardless of editor/IDE
  • No manual setup or configuration, ever
  • Incremental: process only committed changes
  • Bonus: will work in CI too

AUTOMATE ALL THE THINGS

  • npm scripts - "just work" with npm/yarn
    • Part of your dependencies, versioned
    • Single purpose, composable, cross-platform
    • Integrate with editors/IDEs using node's resolution
  • husky 🐶 - "git hooks made easy"
    • Installed with your deps
    • Simple: run commands (npm scripts)
  • lint-staged ⛔💩
    • Runs commands according to globs of git staged files

Enforceable rules, conventions and guidelines are great! They are VERY important. But,

Be flexible

Every rule has an exception

Engineering is about managing trade-offs. When a rule doesn't serve you, change it!

  • Ignore in offending scope: line/block/file
  • Change severity
  • Disable rule

// eslint-ignore-next-line

Refactoring, definition:

Restructuring code, without changing external behavior

REFACTOR ALL THE THINGS!

Refactor/adapt as you go

  • Not "special"
  • Does not require planning or tasks
  • An integral part of daily routine

Trying to program

Refactoring is the air you breathe to keep moving

Here's a haiku I wrote about refactoring

avoiding refactoring

is painful failure

  • Architecture is not special
  • It is just code
  • It is not set in stone or sacred
  • It should serve you
  • It should evolve along with what you're building

Refactoring architecture

Unix philosophy vs frameworks

  • Invest the time to analyze the problem
  • Try to avoid preferring what you know just because it is easier
  • Choose the best tool for the job, instead of the best tool you know

Example:

Choosing a framework/tool because you know it better than something more suitable: SSR vs SPA

"Weeks of coding can save us hours of planning"

Cultivating team & organizational culture

Software development is a team sport!

Invest in people, build trust

People are your greatest and most expensive asset.

  • technological freedom
  • real involvement in product
  • true sense of purpose

Encourage exploration, allow making mistakes, learning and growing. Serious mess-up? Bring 🍰!

  • independence
  • challenges
  • perks are last, least

Give them constantly

Make them want to stay

Trust creates partnerships

Your expectations, the way you give and receive feedback create, encourage and cultivate work habits, which will make or break your team - and business.

People who feel that their work matters will naturally assume ownership, responsibility and care for their work and your business.

You set the standard

Listen and empower

  • Encourage expressing concerns and opinions
  • Ask for feedback, and listen
  • Allow anyone to challenge any decision
  • Nothing is set in stone, anything can be changed
  • Smart people don't need micromanagement
  • Supervise, observe, learn people's strengths
  • Delegate to those who will do better than you!

Let them do what they ❤️

Remote work

Wanna hire good talent? Outsource a project? Get a consultant? Why limit your options to offices?

  • Development is creative, not an assembly line
  • Feeling stuck in an office but worrying about home is a recipe for bad results
  • Let people work when and where they'll do their best!

Why dictate where or when to work?

Enabling effective remote work is hard

But a remote-friendly organization is a better functioning one for ALL employees and collaborators

Mandatory code reviews

For everyone on the team, always - no exceptions!

Coding style, conventions + prettier, static analysis with git hooks allows participants focus on what matters: business and what the code does.

Tooling helps with mechanics

Relationships are more tricky

This is tricky: feedback must be constructive, meaningful and not nit-picking.

Code review rules for people

  • Assume good intent - not ignorance, stupidity or laziness
  • Be nice: how would you feel if someone wrote that to you?

Be constructive

Use a proper tone

  • "How can we improve here?"
  • "Why did you choose... ?"
  • "I would do this..."
  • "We have to..."

Ask and discuss

Suggest or guide

Instruct or command

Early, iterative feedback

Have people submit and review PRs early:

  • Wrong approach to the problem at hand can be caught early
  • Serious bugs or design flaws are caught early
  • Even very partial work should be subject to review
  • Use labels to communicate PR status: WIP, Ready, In review, etc.

Goal: minimize wasted time

Async code reviews

Everyone and anyone can - and should - review others' code in their free time, when they are at ease.

Rejected: waste of time

Async code reviews mandate clear documentation, relevant context and prevent time waste for peers.

Who?

What?

Why?

Async CRs set a record and clarity in every PR. Anyone is be able to understand, review and test any feature or change - without prior context.

What

Who

Task

Details

Notes

Learn from the best

  • Open Source Software is remote, distributed, asynchronous and public
  • Adopt and practice this workflow in your organization and your developers will know how to contribute to OSS
  • Encourage them to contribute to OSS and they will love you!
  • Appeals to candidates when hiring

Improving as you go

  • See something unclear? Clarify it!
  • Took you too long to find that bug? Why? How can you make it easier for the next person?
  • Go the extra mile. Demand it from your devs.

Code improvements should be in practically every change

Boy scout rule

"Leave your code better than you found it."

In a CR, if I see something which I don't like - even though it is unrelated to the PR changes - I'll ask to fix that. It will be reviewed and QA'd anyways.

But how? Ask yourself:

Is it an emergency?

Usually the answer is "No", which means you can do more than the bare minimum

Don't tolerate broken windows

Maintain 0 lint errors and warnings, 0 test failures at all cost!

Manage technical debt

  • When delivering quick and dirty, invest the time to pay back the debts and fix the faults
  • Don't let the bad stuff accumulate
  • Don't get to a place where you're only putting out fires

Reduce bus factor

  • Avoid single domain owners
  • Having experts and those who know some parts better is OK, but
  • Never get to where you rely on a single person, whose work can't be replaced by others
  • Make every part approachable and easy to understand and maintain
  • Document important details

Actively reduce bus factor

  • Shuffle people and tasks
  • Get everyone involved with all parts of the code
  • Start with easy, simple tasks and expand
  • Set a policy which not only allows, but encourages everyone to review anyone else's code
  • Clarify why it matters and allocate time for CRs
  • People learn a LOT from reading others' code

Git workflow

Establish a clear and easy to follow git branching and PR process:

  • Use branch naming conventions. Simple prefixes add clarity:
    • bugfix/description-of-what-is-broken
    • feature/name-of-new-feature
  • Create PRs early and encourage everyone to review other's code
  • Ask people to start their day by reviewing code

Git is hard

  • Make sure devs know git. Git requires a non-shallow understanding to use it effectively. A lot of devs avoid learning it and use a GUI, which poorly abstracts git.
  • When faced with a non trivial issue or any deviation from their standard workflow, they are clueless. Fight it by encouraging using git via CLI and understanding what they actually do when solving git issues.
  • Teach people to make small, atomic, meaningful and descriptive commits

Git: change === branch

  • A strict change per branch policy: even if two changes are in the same codepath, it is safer to have "feature/A" and "bugfix/B" branches. When an integration is necessary, a third branch is due: "feature/A-and-bugfix-B".
  • If the two are closely related, git will allow you to express that relationship by: merging, rebasing, cherry-picking and using patches. Most people don't know this though :(.
  • Back to previous point: devs should know git!

Thank you and keep leading!

Best practices for front-end teams: delivering fast and high quality modern apps at scale

By Nick Ribal

Best practices for front-end teams: delivering fast and high quality modern apps at scale

Speed of development usually comes at the cost of quality: "make it right" or "quick and dirty". The business or client wants everything done today, but too much compromise leaves us a mess of technical debt and a maintainability nightmare. I'll share my recipe for success in enabling high quality software development at a fast pace, based on experience and lessons learned over years of programming, consulting and leading multiple diverse front-end projects and teams. Video of the talk: https://www.youtube.com/watch?v=VSGpXk-1ZgA

  • 1,802