I talk code. Courses: https://t.co/D5emROQa4J YouTube: https://t.co/pWIz4BMXsc Consulting: https://t.co/Qfp4Tfp3jf ⚛️
2 subscribers
Sep 25, 2023 • 4 tweets • 2 min read
Problem: You want to assure your web app isn’t logging messages to the console, and enforce it on CI.
Solution: Use Playwright.
With these 3 lines, I can fail a test if anything is written to the console. I can also be more specific and only fail if a console error occurs.
To make this easy to apply to every test, I created my own page goto function that also applies this console config.
Jun 15, 2023 • 5 tweets • 2 min read
Problem:
When you deploy a new version of your SPA, users with a tab open will keep using the old SPA code.
Solution: 1. Specify the app version in each HTTP call from the UI via a x-app-version header.
2. Validate the x-app-version header on the API server. If x-app-version… twitter.com/i/web/status/1…
If you prefer, you can use HTTP 422 instead. It's more specific than a 400, and feels like a good fit for this use case.
And the header name can be whatever you like. Anything that starts with "x-" is a fine choice.
A bug I see in nearly every web app: If someone saves a stale record, it overwrites the newer DB record. 😬
Solution: 1. Provide 'updated' time with each GET
2. Require 'updated' when sending a POST/PUT
3. If 'updated' doesn't match the DB, return an HTTP 409 (conflict)
When the API returns an error status because the record was stale, tell the user "The record you tried to save has been changed by someone else."
Then, 4 options: 1. Tell the user to reload and try again (Easiest and safest, but annoying for the user)
As a consultant, I work with dozens of teams. I see massive differences in developer output.
Here's an example on the same team:
Dev 1: 45 PRs/month
Dev 2: 1 PR/month
I did code reviews. Dev 1 had higher code quality too.
And yes, stats can be gamed. So, I don't recommend managing based on stats. But when stats are wildly lopsided, there's often a root cause worth exploring.
In this case, the dev who completed 45 PRs had many PRs that were more complex than the 1 PR merged by the other dev.
Apr 1, 2023 • 4 tweets • 1 min read
Continuous integration:
✅ No PRs
✅ No branches
✅ No gating code reviews
✅ Commit directly into trunk
Frequent Integration:
✅ Small PRs
✅ Simple, short-lived feature branches
✅ Code review before merge
✅ Merge branches ~daily
Frequent integration is often good enough.
For many teams, "frequent integration" is the sweet spot.
Doing code reviews before merge assures reviews actually happen. (you can pair, but few devs do consistently, and outside reviews are useful when pairing too)
Integrating work approximately daily is frequent enough.
Mar 23, 2023 • 5 tweets • 2 min read
TypeScript tip: Try to avoid optional fields.
They cause two problems: 1. They reduce type safety. 2. There's no type-safe way to enforce when the field should be populated.
Solution: Often optional fields can be eliminated by declaring separate and more specific types.
Principle: The more information we can provide TypeScript, the more it can help us.
Mar 22, 2023 • 4 tweets • 1 min read
I typically run the app when reviewing a PR.
Here's why:
✅Helps me see the user experience.
✅Seeing the UX helps me read the code, because it helps me understand what the code does, and why.
✅It gives me ideas for checking the code for edge cases.
Reality: Thorough reviews save time. Reviews avoid rework, catch issues early, enforce consistency, and so on.
And ideally I simply run the automated in-browser tests provided with the PR. That takes very little time.
Feb 11, 2023 • 4 tweets • 1 min read
Software death spiral:
“We don’t have automated tests, so we need many environments with slow, manual checks in each.” 😬
Virtuous cycle:
“We have comprehensive tests, so we deploy straight to prod automatically when the CI build passes. We deploy multiple times a day.”🔥
The lack of automated tests means we have to batch work into releases. Batching work means we need multiple environments, with manual testing in each. And we have to plan releases.
Comprehensive automated tests and daily automated deploys eliminate all this overhead. 👍
Feb 9, 2023 • 6 tweets • 1 min read
A developer who immediately approves PRs is a liability. Why? Because they eliminate an opportunity for a *real* review.
A fake reviewer glances at the PR and quickly approves.
A real reviewer reads/runs the code, asks questions, considers alternatives, and makes suggestions.
Many replies boil down to “ain’t got time for that”
I frequently work with clients who want to improve their team’s velocity.
The common thread? The teams are doing few meaningful code reviews.
Glossing over reviews doesn’t save time. It steals time from tomorrow.
Jan 30, 2023 • 4 tweets • 1 min read
The closer developers are to end users, the better.
Why? End user feedback increases empathy, motivates developers, shortens the feedback loop, and enables rapid, cheap experimentation.
Developers working with end users is the epitome of agile.
The first line of the agile manifesto:
“Individuals and interactions over processes and tools”
Developers iterating directly with end users is a wonderful example of agile in action. 👍
I’m a big Cypress fan, so I’m shocked to say this: I just switched to @playwrightweb.
Here are 16 reasons I switched:
1. WAY Faster. ~2X faster with 1 core. ~6x faster with multiple cores (uses multiple workers)
2. Tests multiple browsers in parallel.
1/5
Comparison:👇 3. Uses browser automation APIs, so it's efficient.
4. Simple config, with thoughtful defaults.
5. No globals.
6. Uses plain async/await.
7. Elegantly tests across domains. No extra complexity required. (see pic in first tweet)
8. Supports TypeScript out of the box.
2/5 👇
Oct 31, 2022 • 5 tweets • 1 min read
⚛️10 reasons to prefer useReducer over useState:
1. Separates reads from writes.
2. Avoids useState pitfalls. You should use functional setState to safely update state using existing state. With useReducer, you can't mess this up.
3. Easy to test. It's a pure function.
#react4. Typically simplifies useEffect by removing unnecessary dependencies
5. Centralizes state writes, which is often easier to understand
6. Elegantly handles related state (Note you can also use objects with useState to compose data that changes together)
Sep 30, 2022 • 4 tweets • 1 min read
Tailwind feels as logical as Prettier.
Prettier
✅ Save time by auto-formatting code
✅ Eliminate decision fatigue
✅ Easily switch projects
✅ Code faster
Tailwind
✅ Save time by using predefined styles
✅ Eliminate decision fatigue
✅ Easily switch projects
✅ Code faster
Society moves forward the more decisions we can take for granted.
With Prettier, I don't think about formatting. I don't check it in code reviews. It's a solved problem.
With Tailwind, I don't think about class names. I don't check them in code reviews. It's a solved problem.