Until now (May 2024), I had three experiences working on very big front-end (React+TypeScript) codebases: WorkWave RouteManager, Hasura Console, an...
For further actions, you may consider blocking this person and/or reporting abuse
Great article!! Agree to it all!
Thank you, Jeremy!! 😊
Lots of wise advices!
Thanks
Thanks to you for leaving your appreciation here 😊
This is a great read! Saving this so as to go through all the links later.
Sure, I know reading all of them takes some time 😅
This is gold! Thanks for sharing Stefano. Learned a ton!
Thank you
Amazing article, thank you! I plan to read most of the linked contents very soon.
I have a question - what do you do when your opinions about tests (or readable code) are different than your team's opinions? For example, if you find yourself working with a team that doesn't find the value of tests.
Thanks again :)
That's a great question 😊
I never dealt with such a situation for a long time. Time makes the difference here, because when we speak about the short term, hoenstly there is no differences. If we speak about the medium and long term, instead, tests and code readabilty make a huge differrence.
Anyway, the approach is alwayus the same: I focus on the most important parts to improve (for instance, in a distributed company with a lot of devs, tests are more important than readability of the code itself. TypeScript Discriminated Unions are more important than code indentation, etc. especially if you consider the advent of Copilot etc.) and:
All the above means accepting that 90% of your suggestions (especially if you are a nitpicker) will not be considered at all... But the remaining 10%, the most important ones, maybe yes. And it's a great exercise for me too! Because as a perfectionist, I need to always learn more and more that not everything have the same importance.
What do you think? Do you have different direct experiences? 😊
Thank you for the detailed reply!
I never thought about it, but I totally agree. On larger companies, each team usually owns its own codebase. They usually know who to approach when needing clarifications about the code. What they usually don't know is which part will break when something changes - that's where the importance of tests really shines.
I really like the 90%-10% approach, it makes a lot of sense to me. In some way, it can be parallelized to an important skill of a good developer: understanding what's important, and compromising where needed (e.g. on a quality vs. speed consideration).
I also think of myself as a perfectionist, but I try taking a pragmatic approach. Usually when I review a PR, I tend to be very pedant, and leave comments about "smaller" things as well. However, I make sure to emphasize what's important and what's not, and on the summary note I explicitly say what needs to change to get an approval from my end. I make sure not to become a burden, otherwise people will avoid approaching me.
When taking part on "live" sessions (e.g. design reviews), I try being more "nice", considering what's really important to me, and start by raising only these issues. In some ways, it's a bit harder than doing it "offline", since you need to analyze the details quickly. On the other hand, since it's usually face-to-face, people tend to be more receptive to the feedback.
What's your experience on this regard?
P.S.
I really like the content you write, both the subjects and the style. Keep up the amazing work!
Sorry for the delay, I was on vacation 😊
Could you tell me how you all use it in your company? 😊
That's an interesting approach I did not think about. Here, I always use Conventional Comment to express the importance of every single comment (since most of them are nitpicks).
I do the same 😊
I have the same problem, usually my mind needs a bit more time to analyze things and lot of times I get back to the other dev later in the next hour with more thoughts 😊
Also: it requires less time to convince people face by face than async IMO 😊
Thank you so much, it means a lot to me 🤗
Hey Stefano, Thank you for the reply! I hope you had a good vacation 😊
I'm not sure if it's used by everyone in the company. Personally I keep a mindset similar to what you described: I understand people have a lot on their plates, and not all my suggestions can be applied. Since I understand that 90% of my suggestions may not be implemented, I try hard to find the 10% that are crucial, and should not be ignored (as I see it). It's a matter of prioritization and compromisation 😊
Wow, that's a great convention, I never heard of it! I think that using this depends heavily on the company's culture and size. In a smaller company, I believe it's easier to get a broader agreement about such convention. In a medium or large company, with different groups and sites, it's very likely that not everyone will agree about the benefits of such convention. This may add friction in a worse way than PR comments with no convention 😆
I wonder how larger companies integrate with such conventions in a way that is accepted by most of the workers... Do you happen to know about such processes?
I need to start doing this more 😆 I find myself many times trying to provide a solution quickly, just so I won't have to deal with another thing on my plate. (And perhaps since I want to be seen as "the guy with the answers" 😋). I should follow you as example more often, and take the time offline to think of an answer.
I could tell you that in my experience, it simply happened organically. Who leaves 0/1 comments do not use it. Who leaves a lot of comments start using it when they see you using it. I think it's the best approach instead ot pushing it to everyone 😊
FYI: this takes time, obviously 😊 and I need to balance when to do it and when not otherwise it could ruin my days in a while 😊
Nice article, you pretty much encapsulate all my experiences.
To expand on your typed unions example I think this goes further into correct data modelling techniques.
Too often do I find hacky procedural scripting-like behaviour when people model front end data models but you should be taking as much care as your db, API etc. If application state is correctly modelled it makes the decision of what to guard against or implement in the resulting component so much simpler and unambiguous. Too many front end Devs don't appreciate good OO in this case and it hurts as the apps grow/scale.
I 100% agree, thanks for sharing it 👏👏👏
I have only frontend experience so I can't say if it's something specific to frontend devs or simply lack of "great" mindset, independently from frontend or backedn
Excellent article. Thank you for sharing!
A quick note about your remark on ESLint warnings:
Note that ESLint warnings can show different squiggly lines (yellow instead of red) in your IDE, meaning you can keep coding and fix them later, as they're less distracting. They're only useless if you don't enforce them being fixed eventually.
I would recommend using ˋeslint src --ext ts,tsx --max-warnings 0ˋ as a script in package.json and invoke that from a CI workflow. You could also add a precommit hook for faster feedback on staged files (explained) to improve developer experience.
Thank you, Webber! Did the "warnings during dev and errors in CI and on pre push" approach work well for your team/teams? I would prefer to set a strong alert since the beginning so the developers knows that they are hiding the dust but they need to fix the problems in the short term compared to giving soft warnings and then errors... But I'm very curious about the pros and cons you found with your approach!! 😊
The yellow ones are clearly less daunting and can be helpful abstracting over the exact syntax of your implementation momentarily, which reduces cognitive complexity and allows focusing on the feature at hand.
Errors for
no-console
,react-hooks/exhaustive-deps
andno-unused
, to name a few, can be distracting during development, especially if you can not differentiate them from more structural problems.Blocking them at pre-commit just means you have to fix them before committing. Therefore it does the same thing as you're describing, just with a bit more nuance: multiple colours, but all need to be fixed before committing.
It makes sense, thank you 😊
Great article! I agree on most points, and wanted to emphasize how crucial code style and uniformity of approach is imo. You should ideally never get to discuss these concerns on a PR level, but enforce them before, with automated tools (prettier, eslint, etc).
I agree, one of the next things I want to study is AST for leveraging ESLint for more advanced cases than the ones provided by the various (and great) plugins 😊
I really like your approach and learned some things from your article like the discriminated union.
Personally, I often tell typescript to shut up and turn off noImplicitAny and strictNullChecks. However, I work alone and understand my "loose TS shrinkwrap." I really love how TS infers the return types and I don't mind typing my parameters for the most part.
This changes a lot of things. My approach was the same if yours until I was almost working alone on frontend, I changed my mind when I needed to ensure everything was stable and secure for a lot of devs other than me 😊
This sharing immediately becomes one of my favorite and reference. 👏 awesome!
Thank you, I'm glad you appreciated it 😍
Was there a reason why you spent 2.5 years in a massive codebase with no tests? Did no-one think about writing some?
2.5 because the project was huge, no tests because... We had no time to also invest in tests. For "no time" I mean that we had to carefully choose what to invest in and what not. The team was only partially ready for tests and also the whole project was a big and log R&D process and tests do not play a good role in a R&D phase.
We heavily invested in TypeScript and shared patterns, instead, and even if they are very different from tests, we reached the end goal of always guaranteeing solidity and almost never introducing bugs.
From a testing-oriented one like me, working with such complexity without tests has been a formative experience 😊
More: yes, we thought a lot about writing tests and we started writing them, but the R&D nature and the complexity of the project (web workers, web sockets) forced us us to pause the investment there because we need more time than what we had
I'm sure you know this, but if you have an R&D project, you try and spend a week or two spiking and experimenting, and then get serious writing tests (and abstractions, all the normal good practices). Your 2.5 year project would probably have got more done sooner. Tests aren't an 'investment' apart from the smallest most trivial throwaway project - and even those have a habit of becoming projects
Consider that 50% of the project was an R&D that could be validated only after six months of work (with big initial research, it's true, but in the end, the full project was an R&D one). As a testing fan I would agree with you but... let me elaborate on the kind of tests that we could have added:
E2E: prohibitive, they do not scale in general but especially on this project. More, the QA team was on them, and overlapping did not make sense
Full-frontend without backend tests (Cypress/Playwright testa with a mocked backend): 100% of the communication happened via websocket and there are not great (there are good but not great) plugins out there to simulate this scenario. More, mocking the server and all its peculiarities (even small chinks of the server based on the test's scope) would have been really hard. Last but not least, The 15 MB bundle does not play very well with the browser tests (I also wrote something about optimizing Vite for browser tests here).
Integration tests for the UI part: not very useful since the UI was really dumb
Integration tests for the "server data", the application running in the web worker that is a sort of BFF: this would have made sense, but again simulating/mocking the server and also having readable tests was hard
Integration/unit tests on part of the "server data" application: it would have made a lot of sense! I 100% agree!
But let's take a step back for a moment and let's think about why you accept the complexity of tests (more dependencies, more patterns, something hard for a lot of devs, more "rigidity" in the codebase, etc.): you add tests to
Had we had regressions? Almost never!
The (small number of) regressions we encountered would have been prevented by tests. Partially.
Had we refactored big chunks of code and logic? At least on a monthly basis without regressions.
Had we thought about writing tests? A lot of times and did some spikes but it was really hard because of the generator-based (Redux Saga) nature of the "server data" application.
Had we other urgencies? Yes, if you consider that I made the migration from Webpack to Vite and from Recoil to Valtio during my weekends.
Why the company did not push for having tests? Mostly because the original plan for the optimizations was 6 months.. and turned out in a two-year full rewrite.
Please note that I'm not saying tests were useless! But, for this particular use case, the complexity and the context made the answer to "Should we write tests?" less granted. And I learned a lot, I now can refactor big applications without any tests almost without introducing bugs 😊
Signed by: a testing fan and instructor 😊
Thank you for sharing, there’s a lot of gold to glean from this.
You're welcome, thank you for the nice feedback 🤗
Lots of great advice in this article. Though I haven't worked on a large frontend codebase before, I felt like I could take some of your points into the smaller projects I work on.
Sure, that was the goal! So when the project scale you hopefully will have less problems or, at least, you know some of them before they happen 😊
Ok I was honestly blown away by the typed unions. I’ve never seen anything like that but in hindsight it makes perfect sense. Going to be using that everywhere now!
I see your excitement! They are so solid, so expressive, so ambiguity-free that their wider counterparts (just
string
s and optional properties) feel like... Not using TypeScript at all... 😊