Help responding to coworkers on my technical decisions (NextJS)

I feel like I'm at a point where I'm writing great quality software by blind-following best practices, but I've got little low level understanding that when I'm confronted about my technical decisions I'm not able to give convincing explanations. It's especially problematic since the average dev doesn't really care about the state of the tech meta, so being unable to explain these decisions to coworkers leads to lost potential productivity. I really dread work when I'm being forced to follow poor, outdated, & straight up bad practices - but at the same time I'm at fault for not having the knowledge to move my team forwards. How do I get to a stage where I can convincingly push my decisions towards coworkers? I'm less senior than my coworkers, but I'm passionate about what I do, actively seek the latest trends, & develop stuff in my spare time - whilst they treat their job purely as a job (which is completely fine!), so naturally I have a bit more knowledge about good patterns (but less so to why they're good patterns as you can find out more below).
7 Replies
Y7YA
Y7YA7mo ago
tldr of some decisions I make but which I'm not able to adequately explain
1. Why is embla-carousel (and CSS-heavy solutions in general) superior to swiperjs in NextJS?
2. Why is styled-components a bad practice for NextJS (probably similar reasonings to 1.)
3. Why is prop-drilling not always a good idea.
4. Why root context providers are inferior to zustand.
5. Why conditionally rendering fake nested routes using weird search param logic is not a good idea (aka why nextjs nested routes are better)
1. Why is embla-carousel (and CSS-heavy solutions in general) superior to swiperjs in NextJS?
2. Why is styled-components a bad practice for NextJS (probably similar reasonings to 1.)
3. Why is prop-drilling not always a good idea.
4. Why root context providers are inferior to zustand.
5. Why conditionally rendering fake nested routes using weird search param logic is not a good idea (aka why nextjs nested routes are better)
Context For example, instead of using SwiperJS in NextJS which was my coworker's preference to a carousel library, I opted in for embla-carousel. I made this decision because embla-carousel can be configured mostly with CSS, so layout shifts and weird javascript breakpoint loading issues never occur. Now I don't actually know the technical details to why NextJS renders client components late, or if there's a way to prevent this - so as usual my PR is blocked and I'm forced to refactor and use SwiperJS. Only for them to finally understand the issue with swiperjs after seeing it in action & only then allowing me to use embla. This could've been avoided if I had the knowledge and confidence to explain to them why this is the superior choice. Another example, styled-components is considered a bad practice in NextJS due to SSR. If you ask me why, I'd say performance.. worse devex & layout shifts. But these answers do not actually comprehensively explain why styled-components is a poor choice for NextJS. . Another example is prop drilling. For whatever reason my coworkers love to prop drill everything claiming this is superior for performance, which may be true for expensive requests, but why do I have to propdrill a cookie value I received 7 components deep instead of just doing cookies().get() 1 more time? My explanation for my avoidance of propdrilling was that it couples the code too tightly, makes it confusing for new contributors, and the performance gains they claim are neglible in most cases, but this explanation was inadequate & my PR once again gets blocked in favour for prop drilling. Another example is state management. My coworkers ideas of state management solutions in 2024 are: context & redux, but fortunately the company has moved away from redux. They seem to have no idea Zustand & Jotai exist, & when I saw a good opportunity to use Zustand I was immediately confronted as to why I'm bringing this random library into the project as opposed to just using context providers in root layout.tsx . Now this one I actually technically had no idea, if you asked me then my answer would literally just be: better performance, better devex - not enough to convince anyone. Another example is server-first approaches in NextJS. Like instead of using nextjs nested routes, I find random searchparam logic to render different nested pages. Or I see forms being completely abandoned in favour for unnecessary useState solutions. Again I don't really know how to explain why this is not ideal, as their solutions do technically work. So basically I'm asking you guys for help for a long-term advice on how to handle myself at effectively explaining away things like this, & short-term explanations on specifically the scenarios I have above. Any advice is appreciated.
tylerlaws0n
tylerlaws0n7mo ago
My best attempt at answering: 1. Animating via js typically requires continuous modifications made to the DOM. When considering how to animate things a general principle I tend to go by is if I need a ton of customization and very dynamic logic - a heavily JS approach would make sense. In the case of a carousel though - all you need is a couple buttons with event listeners, the ability to add and remove classnames that represent which panel number is open and the transition style can remain fairly constant in your CSS. The more simple things are - the more you should just lean on CSS. 2. A couple references for why styled components are bad https://romgrk.com/posts/styled-components-bad/ https://github.com/A-gambit/CSS-IN-JS-Benchmarks/blob/master/RESULT.md https://css-tricks.com/a-thorough-analysis-of-css-in-js/ TLDR: css in js solutions add unnecessary overhead and complexity to your code most of the time. Working with tailwind gets you the flexibility of css-in-js (theming, spacing, fonts, use clsx and tw-merge for styling with logic) without the overhead of needing to create a class every time you render your component or something like that. 3. Great post on prop drilling: https://kentcdodds.com/blog/prop-drilling TLDR: It creates horrible, unmaintainable code at scale, Your coworkers point about performance is plainly wrong. Even if they memo every single component, which would help reduce re-renders if those props do not change. Which you should do (as evidenced by the coming changes with the react compiler: https://www.developerway.com/posts/react-compiler-soon). Another good reference on why memoizing all the things can help: https://attardi.org/why-we-memo-all-the-things/ Anyways... back to the original question, if you have a lot of state that you need to pass to multiple components around your tree and also re-render only when the state you use from a given context changes (vs anything in the context changes w/ React Context) use Jotai or zustand 3+. Jotai and zustand give you the nice DX of context with the performance of more targeted re-renders. Other libs are out there to solve this similarly, but it is at the least a clear boost over react context.
better performance, better devex - not enough to convince anyone.
@Y7YA If this is not enough to convince anyone on your team, what would be enough? The abandonment of forms is IMO simply just a bad practice that has popped up on teams that use react out of ease and laziness. Which is a bit self-defeating. You start the form with just an input and a button with a listener, so it's one onClick and one useState with your controlled input... Then the next week you have a task to add validation, then another for a new form field... and on and on. It is easiest to start without a sensible form strategy in react, but as the forms grow, crappy code builds exponentially. 5. I don't think I understand what you mean with this one Long term advice: These decisions are people problems as much as, if not more than they are technical problems. It is easy as a new passionate dev to pickup the latest and greatest tooling and immediately see how powerful, ergonomic, and just lovely the experience can be. When dealing with a team that has been around for n+ years, they have heard these pitches and seen libraries come and go time and time again. When they were in your shoes, they very well may have been pitching their team on (fill in the blank dead library). To work with this and be successful: - Be Patient: Making decisions like this requires building up trust from your team which will be built over time by making incremental, sticky changes that are hopefully long-lasting improvements. - Have empathy: Understand that they have seen things come and go throughout their career - Understand what is important to your team: Typically their key concerns are best solved by the new, but also highly adopted libraries.
Y7YA
Y7YA7mo ago
Thanks for your detailed input! yeah might be more of a people thing as you say but trying to look at it as positively as possible & see what I can improve on my part.
cje
cje7mo ago
mostly agree on whats been said, but i would like to add some counterpoints: - as a less experienced engineer on your team, you get a certain number of "rock the boat points" per quarter/year/whatever. spend them wisely. parts of the application that are working fine are probably not the place to spend these points. - if you make big changes, you need to be prepared to help your team through documentation, pairing, etc. and of course if later on there is some limitation discovered in the new way of doing things then it's on you to solve that - are you creating business value or do you just want to show off a cool new thing? what to improve and what not to improve even though it could be, is very contextual and you figure it out with time some people stay bad at it forever lol in both directions
tylerlaws0n
tylerlaws0n7mo ago
Clarification: I don't mean people in the sense that you work with the wrong people. I mean it is a matter of understanding the people you work with, building trust, and building good working relationships with them. I was using "people" to refer to it as a social problem, not just exclusively a choosing the best tech problem.
Y7YA
Y7YA7mo ago
I think your rock the boat points thing is an excellent way to put it, I guess it's just going to have to be a game of time I guess until my decisions are respected a bit more. I don't ever propose changes for the sake of just proposing changes. It's always something with value, like the other day the teamhad a little argument, 1 side wanted redux the other side wanted to keep nesting providers in a root-level provider as it's simpler than someone new joining the project and learning redux. I come in and I'm like, how about we use zustand? takes 2 minutes to learn, and does everything we'd expect from redux - best of both worlds. Ofcourse I was denied by both parties because "what even is that?". one other big one was the team kept running into issues creating accessible components, and it was causing a lot of back n forth, I suggested they use radix but nope let's stick to reinventing the wheel and failing until we stop failing I see the team having problems, I suggest proven solutions, solutions get rejected, repeat
cje
cje7mo ago
You get trust on larger decisions by showing that your smaller decisions pay off. If not, search for a different job.
Want results from more Discord servers?
Add your server