It seems the big selling point is building websites without JS.
To each their own but typescript is one of the best programming languages I’ve used. I do enjoy python a lot but whenever I use it I feel like I’m going a decade into the past, especially with their tooling (lint, types, formatters, package manager, venv, etc).
I love TypeScript, and I think React is mostly very elegant, and I still find front end work to be a real chore. Fucking around with forms and CSS and responsive breakpoints is not my idea of a good time!
You shouldn't need breakpoints 98% of the time. Use flex box and grid. Failing that, try container queries.
A really good form is still a PITA. I've been trying to perfect it for years but haven't found anything to sync frontend and backend validation that doesn't suck/involve a lot of boilerplate.
Well, for my latest iteration, I'm using Bun+Elysia on the server which uses Typebox for request validation+schema, which is effectively JSON Schema so you could do something similar in other languages. I can pass that schema down to the client/browser, and I could use AJV to run that schema against the request before I send it to the server, but there's a couple tricky parts.
1.Json schema/AJV return not very human friendly results and they're hard to associate with a specific field to display the error nicely
2. If you want to validation as the user is typing to clear errors as they're satisfied, you have to run the entire schema, not just for that one field
3. Any async validations like checking if a username is unique is harder if not impossible with this approach
No, hadn't heard of that. Looks like it's doing a bit too much. I'm not sold on ORMs or auto-genned scaffolding apps. The tRPC lib they're using OTOH looks interesting. Kind of. Looks like I'd have to give up my server lib/router for theirs, but then it looks like they botched the client-side error formatting which is the one thing I haven't solved: https://trpc.io/docs/server/error-formatting How is this good UX?
Formik (https://formik.org/docs/api/errormessage) does OK with the "touched" and "errors" objects, that helps you render out the error messages nicely, next to the inputs, but for my very first use-case I wanted to validate a File input and it seems to struggle with that a bit. Can't do it without suppressing some TS errors but maybe it's an edge case. And it of course doesn't handle the server half of the equation. So.. yeah.
This looks excellent, thank you. I'm iterating a lot of startups at the moment, and sort of home-growing a framework, but each new product I want to replace bits of it with off-the-shelf tooling, and this is an excellent selection of components.
I don't think the main problem backend developers have with frontend work is the language. As you say, Typescript is a really good language - in many ways better than Python. I don't think it's even the tooling, although becoming basically competent with two very large tooling stacks is not a trivial matter either.
I think there's a fundamental verbosity to the way most SPAs do data management - I've seen it said elsewhere that when you build a SPA, you basically end up having to reinvent the database, by which it is meant that an inordinate amount of your code is dedicated to extracting data from the backend via an API and then keeping track of that state using mountains of front end code.
At some point, you have to decide - is this really two separate applications (a backend and a frontend), or is it actually just one? If you don't have a need for multiple different clients, and you already have a backend written in Python (or some other backend language, e.g. Elixir, Kotlin), then maybe you can get away with writing a whole lot less code if you can find a framework that fills in that whole in-between layer for you and lets you stick with the language you already have.
Ah! A real criticism of FE development, I agree with your problem statement.
When you jump into the world of single-page applications, things get complex pretty quickly, because the use case for needing an SPA pushes the web app into a full desktop application.
Ultimately, for a highly interactive and dynamic "desktop-class" user experience, there is added complexity. I think that's why so much movement within the FE world has moved away from "SPA for everything" and into these mixed dynamic apps. Islands, React Server Components, NextJS, they all help create a middleground between a document-based website with no dynamic elements with a full blown desktop app experience. They all have real tradeoffs, in particular adding an entirely new backend service to serve the front end.
For many projects, react + react-query is probably enough.
Having said that, my argument from https://bower.sh/dogma-of-restful-api still stands: when you build an API that is RESTful (1:1 mapping between endpoint and entity) you are unknowingly pushing the complexity of data synchronization to the FE, which requires a well thought out ETL pipeline.
This probably doesn't help my case but I've been building a simplified middle-layer for react to bridge the gap between react-query and full blown SPA: https://starfx.bower.sh
It is all an illusion. Unicode, ASCII, base 10 numbers, are all lies. Our CPUs know binary registers of certain sizes, ints and floats, and nothing else.
Everything else is a lie that we make work by throwing a lot electricity at a lot of fancy sand and out the other end comes something that looks reasonable.
That said, TS has a really nice type system that is absurdly flexible and at times quite fun to use.
I say the phrase "TypeScript crimes" a lot, and it is almost always a positive.
Most programmers I work with and have worked with are not wielding the type system in the same way, and that's usually okay--because I tend to be the one building the libraries they then use, and the compiler keeps folks from doing things with my code that I didn't plan for.
I have some bad news for you about the types in Python--which, for the record, I really like and have made me willing to write Python!--if your objection is that the types are "just an illusion". Much like Pydantic, good use of TypeScript types involves shape validation on input.
(I have worse news for you about C or C++ or anything that isn't typed exclusively based on how wide its register is, but that's nitpicking.)
Honestly, if I'm going to nitpick myself, even that isn't a true statement. You might have an integer/float divide for your registers, like amd64 does, but even past that it's squishy and becomes human bookkeeping or compiler magic. Is %rax an 8-byte integer, or is it a pointer? Or is it garbage because you used %eax (the lower 4 bytes of %rax) for storing a 4-byte integer, and the top half is whatever(tm)? Ditto again for %ax (2-byte) or %al (1-byte). You have widths, but me calling them types was really even a stretch there.
And then everything else is just built from that, really. Strings/arrays? Either length+bytes/charpoints (hope you counted correctly) or "bytes/charpoints until you hit a NUL" (hope you put the NUL in or now your buffers are spewing somewhere they shouldn't). Structs? Pile of bytes, each of them special-cased by the compiler. (A polymorphic class would then have some virtual function tables somewhere too.) And if your language has a fixed-point decimal class, it's time to get weird.
Types are a really convenient, really helpful concept, but to sniff at them being compiler-only in something like TypeScript or typed Python is IMO misguided--because compilers have gotten pretty smart and it allows us to reasonably make flexibility tradeoffs because those compilers are good at doing things. Java will scream at runtime if I cast an object to something it can't be, and that's great, but also, I just won't do that if I validated my inputs in Python with something like Pydantic or TypeScript with something like Zod or Typebox or whatever, because an illegal action with a type becomes a compiler error. So, IMO, let's get over the argument and get back to making stuff. (Even the Golang people. All is forgiven, mostly.)
The problem with Python specifically is that the type system is such a massive bolted-on hack. It has to be, because it's such a dynamic language that all declarations and imports are executable statements - and it's hard to reconcile it with type declarations, which often need to be mutually recursive.
It is kind of hacky for sure, but I've gotten way more productive with it than I've ever been with Python before. Like I went my entire career (and time before that) where the words that came out of my mouth after "Python" were "is bullshit", but this year due to both AI jank and other unrelated tools having no path but Python, I've made my peace with it--and yeah it's spotty, though with Pylance I think it's fine, and it saves me from so many category errors that make old-Python infuriating.
Unfortunately for me, as an unreconstructed Ruby dork, Sorbet does not scratch the same itch. :(
Hate to break it to you but that's how all languages work. And if they don't they are just wasting CPU cycles checking types anywhere but the edges of the app.
To each their own but typescript is one of the best programming languages I’ve used. I do enjoy python a lot but whenever I use it I feel like I’m going a decade into the past, especially with their tooling (lint, types, formatters, package manager, venv, etc).
I wrote a post recently about how I feel like this loathing of all things FE/JS is overblown: https://bower.sh/front-end-complexity