4 ms·
> huge, enormous, gargantuan benefits Would you mind enumerating them? I have an idea of what they are but curious about other perspectives. My sense is that
by sa46 5y ago
> huge, enormous, gargantuan benefits
Would you mind enumerating them? I have an idea of what they are but curious about other perspectives.
My sense is that code sharing is not that common between a Typescript frontend and backend. You mostly need generated request/response data types but I don't think there's that much shared behavior because you can't import any of your backend-y logic (database, auth, external APIs) transitively into your frontend.
I think primary gain is what you've hinted at: 1 ecosystem and it's easier to onboard fullstack devs.
- jitl 5y agoNotion shares a large amount of code between the front-end and back-end. We have many algorithms, collections, helpers, etc that we share. Here's an example, we have a shared loadPageChunk function that takes a cursor and a loader implementation, then traverses our data graph to gather the data needed to render part of a page. // shared code - implement the algorithm export async function loadPageChunk( args: LoadPageChunkArgs, loadRecordValue: loadRecordValueFn ) { // ... } // client code - use the algorithm, provide client-specific IO // eg on Android we'd use Sqlite. const records = await loadPageChunk(cursor, SqliteService.loadRecordValue) // Server code - same, but use the server's data stores. // Behind the scenes, these loaders batch, etc const records = await loadPageChunk(cursor, useCache ? CacheService.loadRecordValue : PostgresService.loadRecordValue) Even if we only shared types, there's a significant benefit. We try to push as much logic into the type system as we can; for example we use discriminating unions to define different groups of related types. Eg, we have a union type called ContentBlock that has all the specific block types that can have children, `Page | Text | Column | ...`, sharing this type and its helper functions like `isContentBlock(block: BlockValue): block is ContentBlock` means both our front-end and back-end code rely/expect/enforce the same invariants.
- hn_throwaway_99 5y agoOne example: input validation logic is something that is nice to run in the front end (for performance and immediate responsiveness to users) but that you also need to run in the back end for correctness. Previously I've done this with 2 implementations (JS on frontend and Java on backend), but then keeping the logic in-sync is a nightmare. With a single language you can just share the library. You are correct, though, the biggest benefit I see is not sharing code, but making it trivially easy for a front-end dev to add a small piece of backend code that they need without needing a back-end dev to do it, and vice versa. It just makes the overall team much more productive because there is very little "waiting on the back/front-end" to do it. Oftentimes we'll have either team write up the schema for a new endpoint using the GraphQL schema definition language, then from that we autogenerate the TypeScript types, then usually the front-end team creates a simple mock in the backend so that they can fully implement the UI, meanwhile the backend team works concurrently on the real implementation. This process allows for much more parallel productivity than if, when something is broken or needed from the other team, you just have to submit a ticket and wait.