[1] The one exception is SQL DBs and redis, but I have always been fine using brew, linuxbrew, or apt for them.
This is subtle. There are more bumps along the way. However, the task of upgrading isn't neglected forever. Instead, it usually seems that each new member winds up upgrading some small piece and making sure the code works with a small change in environment.
By default, Docker runs everything as root. This isn't a huge problem on macOS or Windows, where Docker runs in a VM and there's some sort of UID mapper mediating things. If a file is generated from a process within Docker (e.g., temporary or log files) and you're running in Linux, now you have files in your local system that you can't edit without "sudo". Fine, don't run the container as root. I'd have expected that to be a best practice, but it doesn't come up in Docker's best practices guide [0]. Adding our own user and switching to that isn't a huge hurdle, although this didn't seem to be an area that we'd have to chart our own path. Unfortunately, some 3rd party images don't run particularly well, if at all, if not run as root.
Once we got that running, we hit our next issue. Although things were working well for one developer, they broke for another because there still existed the same basic problem with Linux users ending up with files owned by UIDs other than their local account. It turns out that not every dev is running with the same UID. Okay, so now we need to map the UID and GID at image build time, but that might break things for people on macOS.
All of our Dockerfiles ended up with something like:
ARG app_user_uid=61000
ARG app_user_gid=61000
ENV APP_USER="app"
RUN groupadd -g $app_user_gid -o $APP_USER
RUN useradd --no-log-init --create-home --shell /bin/false --gid $app_user_gid --uid $app_user_uid -o $APP_USER
And needed to be built on Linux with: docker-compose build --build-arg app_user_uid=$(id -u) --build-arg app_user_gid=$(id -g) ...
While macOS users used the simpler: docker-compose build ...
This all took quite some time to figure out. The person working on it would get things working, think it was all good, push it up, and only find out later that it wouldn't work on another dev's system. CI couldn't catch everything. The point of using Docker was to ensure there weren't inconsistencies against dev environments and that dev matched production. That seems like a fairly common use case, but we couldn't find anything on how to simplify this setup for teams other than mandating every user run the same exact system configuration. I have to believe we were doing something wrong, but we really couldn't find anything on the topic. I'd love to hear how you solved the problem.Then think of it this way: it became easier to make the content/UI both good looking and fully accessible (e.g. to screen reader users, for whom layout tables are indeed a hindrance).
I think the same applies to the other hoops to jump through; they're solving a real problem, just perhaps one that doesn't directly affect many of us so we tend to ignore it.
I suspect that the giant explosion in primitives for a screen reader to deal with had actually made the job harder.
And this is ignoring the explosion in nested divs that most modern sites have.
So, it's true that screen readers have heuristics for detecting layout tables. But those heuristics aren't perfect. They're not even that sophisticated, at least the ones that I worked on. And some (most?) screen readers automatically announce any table that they don't detect as a layout table, even when continuously reading straight through the document. Notably, the screen reader I developed myself, which was the first one I routinely used, doesn't automatically announce tables unless the user is manually moving through the page, because I wanted to minimize verbal clutter when casually browsing the web. But when I went to work at Microsoft and started using Narrator to read my work email, I found the tables in certain HTML emails very annoying. Granted, Narrator didn't yet have a layout table heuristic at that time, but even when it did, that didn't entirely solve the problem. Luckily, there was also an overhaul in Narrator's verbosity levels while I was there, and that allowed me to eliminate the clutter of layout tables by reducing the verbosity level in my personal Narrator settings, perhaps at the cost of missing some other things.
Nested divs per se aren't a problem. Implementing a widget as a div without the appropriate ARIA markup is a problem.
Html email feels like a trap. They are about turning up the volume and getting returns. Not about being accessible. Is that not the case?
My gut is still that better tooling support for standard messages would have helped faster than more primitives in the message. But, I welcome evidence that I'm wrong.
Nested divs aren't that bad. (Ignoring overall performance considerations. And even then I'd suspect that's less a concern than download and running all these JS libraries.) Nested divs with a mish-mash of poorly applied ARIA can get pretty bad though!
Table layouts are also terrible for anyone who is not viewing the site at the browser width you designed it for. Accessibility is more than screen readers, and the ability to zoom or adjust browser width or display on a different device (hello mobile) is incredibly valuable for a lot of people. And I'd argue that the "ease" of using table layouts disappears as soon as you try to accommodate for any of that.
https://es.discourse.group/t/callback-based-simplified-async...
It seems quite inneficient to me to wrap every function call in a caching layer and state machine that pretty much goes unused in the context of async/await and makes things unecessarily hard to undertand for new programmers.
> [...] gotchas of .NET Core async/await APIs drove me away.
I kind of lost track of the framework to core changes but I think it became simpler with the removal of synchronization contexts?
Writing parallel code that isn't clearly a divide and conquer variant is hard. Async doesn't help with that at all; it gives you more efficient context switches.
I personally find threaded code easier to reason about because there can be a context switch anywhere, so you always need to think about locks and coordination. With aysnc code, a block of code without locks and be correct...until you add an await in the middle, and this isn't always obvious.
> SSL certificates, with Let’s Encrypt
Semi-mandatory SSL certificates weren't a thing on the web until fairly recently. Not managing them at all was certainly easier than managing them with Let's Encrypt, which has a lot of gotchas, requires tooling, knowledge and can suddenly break your website if something is off.
> Concurrency, with async/await (in several languages)
async/await usage is literally the main reason why my recent project is written in Go instead of C#. I like C#. I don't like Go. But dealing with all the gotchas of .NET Core async/await APIs drove me away.
Moreover, Erlang had a much more sane and powerful parallelism model way before async/await.
> Centering in CSS, with flexbox/grid
Again, like with SSL, this is technically correct, but generally incorrect. Centering in CSS has gotten much easier with flexbox/grid. However, centering in HTML only became hard because tables were abandoned as the layout mechanism. Sure, they're not "semantic". But we're talking about what became easier, right? Not about what became more ideologically correct, semantic, etc.
> Configuring cloud infrastructure, with Terraform
Again, there used to be no Cloud infrastructure to configure. Personally, I find Terrafrom annoying more than anything else.
> Setting up a dev environment, with Docker
As opposed to doing what? Keeping my dev environment simple is a constant battle and most of the time Docker is the weapon used by the other side. In 00s my dev environment was a text editor and some upload tool.
I haven't used PHP in well over a decade, but for my latest personal project I've decided to brush up on PHP 8.1. Despite awful syntax, I can do stuff by writing a handful of lines of code in a text editor. It's very refreshing. 100% focus on the outcome rather than tooling.