I am surprised to see this article translated by author to Polish. It is not perfect Polish (I am native speaker) but still super impressive for someone that is learning Polish as 2nd language (let’s say, that this is not the easiest language to learn).
My wife and her family are Polish— although I rarely discuss hash functions with them!
I see my articles posted to Polish forums and sites sometimes. I assumed that the Polish tech scene, although burgeoning, is presently somewhat underserved in terms of accessible content, since many of the largest tech organizations work and write primarily in English. I’ve started to translate my blog posts even (especially) when they are particularly technical. Several kind Polish speakers have provided generous corrections and discussion of my grammar and word choice, including a commenter on this very blog post.
Some of the Polish leaks into my English word choice, such as using “some” instead of “a” or “any” in more situations than I would have before learning Polish.
I also see my articles on Chinese and Japanese sites, but those languages are a little too much for me to learn right now 😂
“although I rarely discuss hash functions with them!”
This implies that you do, occasionally, discuss them.
My wife and her father work in tech, so sometimes technical topics do arise! Perhaps I once had to explain the blockchain in Polish?
The post author, @arxanas, is the creator of git-branchless, which is the most ambitious layer over git that I’m aware of. He ported revsets from mercurial ffs. I don’t think he likes easy things :p
Wonder how this differs from https://github.com/jetpack-io/devbox
One difference is that devbox wants to almost completely hide nix from you. For example, it automatically installs it for you. The abstraction is thicker, for sure.
My very naive guess at which you should play with, based on how I decided: if you want an abstraction over nix that you can adopt without ever hearing the word nix, give devbox a try. If you want something to ease you into using nix before you dive in head first, give devenv a try. I have very low confidence that’s a good or accurate heuristic.
Hi all, I’m the founder of jetpack.io (developers of Devbox), and I agree with your take:
Devbox is trying to give you the power of nix, but with a simplified interface that matches the simplicity of a package manager like yarn. If you want reproducible environments by simply specifying the list of packages you depend on, and not having to learn a new language, then devbox is a great match.
On the other hand, if you’re trying to do something that is involved and requires the full nix language, then devenv can be a great match.
and it uses json as a surface language, with all the limitations this implies compared to nix-lang.
yea, that’s what i surmised just from a cursory look… the devbox abstraction might be quite limiting in some ways, whereas devenv seems thinner and probably therefore less leaky (because it’s designed to leak?)
I feel like this is misleading (I’m the founder of jetpack.io the company that makes Devbox)
Yes, jetpack.io is a for-profit business: but we’ve committed to making the Devbox OSS tool free forever. We plan to monetize by offering managed services (Devbox Cloud), but those are an independent codebase, and you only need to pay for them if you want the managed services. If you don’t, then Devbox OSS is, and will forever be, free and completely open source.
This is similar to how Cachix is a for-profit business. It offers a Cachix managed service that it monetizes. It is commited to making devenv a free open-source project. You only need to pay for cachix if you want to use the managed service.
In that sense, both companies/projects monetize in a similar way.
Oh hey, yet another person ruining the word “gaslighting”. Nobody’s pretending that people weren’t hyping angular before react. Nobody’s telling you your memory is wrong. People were hyping angular, and now they’re hyping something else. That’s not gaslighting.
And you know what? That’s not even the biggest problem! The article keeps saying that the react hypemen were angular hypemen 10 years ago. But it never shows this. It never quotes a person hyping angular, and then later pitching react. For all we know, these are two separate groups of people. And that would completely undermine everything the article is trying to say. It’s not gaslighting for different people to push different things!
At the bottom of the article he’s advertising paid courses on vanillajs. I can only assume he’s secretly gaslighting us, too.
I mean this nicely, but you’re kinda sealioning here w.r.t. web stuff. I’ll vouch, for whatever my anecdata is worth, that at many employers, countless conversations, and many spaces there has in fact been a lot of what you euphemistically refer to as pivoting and the author here refers to as gaslighting. It is a real phenomena, and the folks doing it would love nothing more than to have useful idiots providing cover fire for them.
By your own admission:
I don’t give a single shit about React versus Vue versus Vanilla JS.
Okay, cool–please respect the lived experiences of the people who have had to deal with that shit day in and day out for over a decade. If you want, I can provide horror stories (about this, about React, about Apollo, about GraphQL, about Angular, about many other things in webshitland)…but please do not categorically dismiss an entire type of issue that is in fact so pervasive that finding isolated textbook examples of it is quite difficult.
Whenever something takes the developer world by storm, it’s never because we banded together to puppeteer your brains […] don’t read a shadowy cabal into every zig and zag of the tech industry.
What you did here is what we vulgar unwashed masses would refer to as gaslighting. A lot of money and marketing resources were spent promoting React (and Angular, and Mongo, etc.) by corporate interests, even before the secondary market of folks selling services for consulting and training and bootcamps existed. This author (and I, and others) commented on this undeniable fact.
If enough of us have seen this play out, and a few of us say “hey there’s a thing going on here”, and here you are saying “silly developer, pattern matching with abandon; ignore your experience, there is no conspiracy here!”…you’re kinda gaslighting, my guy. It’s not a good look. (This has, incidentally, played out over and over again across all corners of public life, and I wager is a good part of the distrust of experts.)
( And while I do think you make a reasonable point about trying to get the meaning of gaslight precise, well, in $current_year we have ceded that ground on so many words I really don’t think one more is going to make a difference. We collectively can’t even agree on what violence is, so let’s not clutch pearls about gaslighting versus lying–what matters is that people are hurt through a cluster of behaviors and wish to have their feelings recognized and respected. )
What you did here is what we vulgar unwashed masses would refer to as gaslighting.
Only if you’re making the word useless by turning it into a synonym of “denying”. The whole point of gaslighting is t psychological manipulation intended to make the person doubt their own memories. Simply arguing against them, about public events that are a matter of record, doesn’t count. Otherwise hwayne could claim you’re gaslighting them back, and then you have a dumb little circular “youre wrong!” “No, you’re wrong!” thing going on.
As I understand it, gaslighting can be a spectrum somewhere along the lines of:
(I am not a shrink, I am not your shrink, etc., but these three levels are kinda how I reason about gaslighting.)
“Proper” gaslighting is the first one of those, but insisting on receipts as corroboration or repeated “there are no conspiracies anywhere” can get in as a lighter form of the second category. There’s definitely a fuzzy line between that and mere disagreement, sure, but I think the difference comes out in the wash and it’s worth erring on the side of addressing it.
I’m in agreement with Hillel that gaslighting should describe an element of intentionality that’s missing in the article, and IME the word hasn’t yet reached that. I also think its usefulness in describing abuse is diminished if the word is watered down.
Of course, the article itself and very fact that you think gaslighting has a spectrum, suggests it may already be too late.
Yeah, there is actually very little overlap between those groups of people.
This recent spate of anti-React trash getting upvoted on lobsters is really embarrassing. Like, I get the dogmatically anti-SPA tilt of the site’s user base, but at least upvote good critiques. There’s so much you could rant about that would be valid but these vapid articles wouldn’t even begin to know what those things are.
I’m pretty sure that banning the app from the two major app stores would be enough to kill TikTok In the US.
Either that, or we’d suddenly have normies actually using alternative app stores in large enough numbers to make them matter, weakening the duopoly’s stranglehold on mobile software distribution. I certainly wouldn’t mind that outcome.
We’d also see a rise in normies getting malware installed on their phones. The amount of malware that still makes it into the official app stores is already too much.
I doubt it. The rate at which malware makes it onto the Apple App Store and the Google Play Store far exceeds that of most other moderated software distribution platforms.
There’s another catch-22, also related to TikTok, which I think shows just how unnecessary the whole “national security” angle is. I don’t know if TikTok poses one or not – I think that, in fact, is mostly irrelevant when it comes to data collection, for reasons that Schneier already points out in the article so I’m not going to repeat them.
A while ago TikTok caught some flak because someone found out the Chinese version also has a bunch of educational content for kids, which it pushes to young users based on some timing and location rules. It’s obviously attractive to frame this as a conspiracy to brainwash Western children’s minds, but the explanation is a lot more mundane: Chinese legislation forces all media companies that offer content to children under a certain age to include some proportion of educational content. Western countries could enact similar legislation – or, in the case of some of them (including, I believe, my home country), re-enact it, as they once had such legal provisions and eventually dropped them.
Same thing here. If TikTok does, indeed, collect data that poses a national security risk, at least some of that data is bound to be data from some high-interest people’s devices, not from everyone‘s device. There’s no way collecting that data from a handful of people’s phones is risky for national security, but collecting it from everyone’s devices is not risky for consumers’ and private citizens rights in general. Most Western countries already have the constitutional basis to ban this if they want it.
The “national security” risk angle is related to TikTok’s ability to control narratives, shape thinking and potentially influence elections in the US. Of course, this can be argued about FaceBook and other social media companies, but none of them have the share among young people as TikTok does.
Yeah because facebook wasn’t a hotbed of misinformation for older conservatives. Like. Facebook is notoriously awful about letting troll farms just manufacture whatever narrative they want and it just not being questioned.
Those old conservatives you clearly despise (don’t worry they’ll be dead soon) are citizens. The CCP are not.
Yes because citizenship is the issue here/s I’m all for placing restrictions on social media companies to have responsibilities wrt misinfo but like. Fox News exists in America, we don’t get to point fingers here.
Many countries/regions have rules restricting “foreign” apps/companies/etc. from receiving or processing their citizens’ data.
Not saying that it’s good to do that, but it’s not some sort of unprecedented new thing here, and any reasonable discussion about it has to understand and account for current practice.
A while ago TikTok caught some flak because someone found out the Chinese version also has a bunch of educational content for kids, which it pushes to young users based on some timing and location rules. It’s obviously attractive to frame this as a conspiracy to brainwash Western children’s minds, but the explanation is a lot more mundane: Chinese legislation forces all media companies that offer content to children under a certain age to include some proportion of educational content. Western countries could enact similar legislation – or, in the case of some of them (including, I believe, my home country), re-enact it, as they once had such legal provisions and eventually dropped them.
Sounds like the Chinese equivalent of https://en.wikipedia.org/wiki/Regulations_on_children%27s_television_programming_in_the_United_States , which apparently subtly regulated a bunch of things about TV shows I remember nostalgically from my 90s childhood, and which seems pointless to me in retrospect. They should’ve let Weird Al be funny on TV for kids without having to shoehorn in content that technically counted as educational to fulfill a legal requirement.
IIRC, this is why GI Joe had the little “and knowing is half the battle” skits at the end of every episode, so they could cynically claim to be educational. I think Masters of the Universe had the same thing.
which apparently subtly regulated a bunch of things about TV shows I remember nostalgically from my 90s childhood, and which seems pointless to me in retrospect.
Prologue + Part 1 of this article is required reading on this subject, from a person who grew up in the 80s.
Western countries could enact similar legislation
I don’t want to imagine the kind of children’s educational content that would be legislated, especially in certain areas of the country.
Oh, I’m not implying it’s a good idea. I bet the educational content they push in China is… kind of on par with the kind of children’s educational content that would be legislated in certain areas of the country :-P.
The point I was trying to make is that the delivery of “brainless” entertainment content is not inherent to a platform’s technology or algorithms. Those don’t exist in a vacuum. TikTok, like many other platforms, is doing a bunch of things because #1 they’re allowed to do it without any restrictions and #2 because management thinks it’s a good idea. It’s completely unproductive to create the conditions for #1, and then fuss about how it’s used. As long as #1 holds, anyone who also ticks #2 will handle it the same way, so simply banning the current market leader will just change the market leader, not the things it does.
Similarly, gathering data to facilitate misinformation campaigns is not inherent to the technology TikTok uses, or the algorithms they use. It’s something that TikTok, and other social networks, do because they’re allowed to and because it’s good for their bottom lines. Banning a company, but not the practice, just makes room for another company to engage in it.
It’s kind of like school admins trying to ban chat programs because they interfered with students’ activities back in the day. When ICQ was banned and filtered, we just moved to Yahoo Messenger.
Regardless, Safari 16.4 is astonishingly dense with delayed features, inadvertantly emphasising just how far behind WebKit has remained for many years and how effective the Blink Launch Process has been in allowing Chromium to ship responsibly while consensus was witheld in standards by Apple. It simultaneously shows how effective the requirements of that process have been in accelerating catch-up implementations. By mandating proof of developer enthusiasm for features, extensive test suites, and accurate specifications, the catch-up process has been put on rails for Apple. The intentional, responsible leadership of Blink was no accident, but to see it rewarded so definitively is gratifying.
I found this pretty boggle-worthy - the repeated use of the word “responsible”, painting blink/chrome as such a bastion of good internet citizenship while pushing their own standards, several of which have serious end user privacy concerns (web usb, etc). I mean, how dare everyone else not ship chrome’s de facto (“we have market share, so we can make our own standards”) standards right away?!
After clicking “about” on the blog it makes a bit more sense how/why the author might make such an assertion.
Yeah. I also note that the “delayed features” that are supposedly holding back the “Open Web” are on average things that landed in Chrome in 2018-2019. It’s as if the browser-pushers want us to think that the web of 2018 is obviously intolerably backward and unusable. Bro, please.
It may have snuck up on you, but 2018 was five years ago. I would count a feature that fails to land on major platforms for that long as effectively dead. It’s not that 2018 was the stone ages, but you don’t want to be in 2018 forever.
you don’t want to be in 2018 forever.
Other than security upgrades and fixes, why not? What have we gained in the 5 years since that hasn’t primarily been in service of companies like Google extending their data gathering / advertising pimpage?
WASM threads were released in Chrome in 2019, I would guess many other improvements to WASM have been released as well.
A lot of mobile web apps are DOA without the Push API which shipped in Chrome in 2015 and Firefox in 2016. They are a big part of this Safari 16.4 release.
Chrome may be abusing its dominant status to push defacto non-standards but Safari is actually very behind.
A royalty-free video codec that isn’t ancient and doesn’t suck. HTTP3, which makes a real difference to performance, especially on iffy connections. Lazy loading hints so that heavy content doesn’t have to be loaded unless the user is actually going to see it (without lag-inducing JS hacks). Motion sensing support for phones (or whatever devices have gyros/accelerometers.). Tools for making layouts that aren’t ass-backwards when the content is in an RTL language. Some more stuff in general for making pages that look nice even without a gigabyte of tool-generated CSS and JS.
And of course, every millennial’s favorite, the ability for a media query to check whether the system-wide “dark mode” is enabled.
One of the most important things about an open ecosystem which ties in closely to responsibility is sustainability. The level of resources Google puts into adding features into Chromium is nigh-impossible to match sustainably. This isn’t to say that Apple doesn’t have a) the resources, or b) reasons to de-prioritise features, especially ones that duplicate native application functionality to their lucrative App Store, but even MS pulled out of this game with a reasonably competitive modern browser engine in EdgeHTML.
Given how much sway MS/Google have over the browser market with their position in WHATWG and Chromium/Blink’s market share, it’s not really much better than the “dictated standards” of the past, like PDF or OOXML - having an open spec to meet regulatory requirements many governments have about “open data standards”, but driven entirely by stakeholders who effectively control that ecosystem.
It’s the old “Fire and Motion” strategy that Joel Spolsky used to write about in the context of Microsoft. Google can simply fire bursts of new “standard” features at everyone else, and then blame competitors for being unable to keep up. And developers will happily take up the chant of “Safari is the new IE! The new IE! The new IE!” despite the fact that it’s Google and Chrome using the old Microsoft tactics.
This is a really silly conversation and I’m sad to see it keep coming up over and over again. The people building huge, performant web apps with hundreds of millions of users aren’t having this conversation.
The things that are slow about React and JavaScript applications are not bottlenecks in React or JavaScript.
I’ve done a lot of web performance work and it’s always the same shit on the UI side:
React doesn’t cause (or even encourage) any of this. The ecosystem and engineering happening around React are often garbage (at least in open source) and that’s the only reason these problems exist.
Look at how facebook.com, rebuilt in 2020 to be a modern SPA, works:
React doesn’t get in the way of doing that kind of good engineering. In Meta’s case, React made all the good behavior above easier to accomplish. If you do UI work and you use React, don’t worry: you haven’t been bamboozled. If your SPA performance sucks it’s not cause you’re using React or because you built a SPA.
One of Russell’s main claims is that React is being served to millions of users with low-powered devices for websites that are neither huge nor well-engineered. He talked about how Facebook has the resources to do it well, but most don’t, and there’s the snake oil. I think the case can get overstated, and that’s what this article addresses, but this sounds dismissive of a vast number of the world’s mobile users, and sounds like a palliative to the people building websites that uncritically and unnecessarily import hundreds of kbs of JS, which is very slow to parse on these devices.
Okay, let me make some concrete assertions:
If an engineering team, with any amount of resources, doesn’t care about performance, then no technology stack will help. The performance of their app will suck.
There are tools to drastically improve the performance of applications that no one uses. For example, Meta open sources Relay which is an absolute gem and no one uses it. Meta publishes specs and gives conference talks about GraphQL best practices. A lot of the engineering that went into the facebook.com rewrite has been open sourced and discussed publicly.
Snake oil is something that doesn’t work. The modern SPA tech stack is amazing and works. These people aren’t snake oil salesmen: they are philanthropists giving away gold that really works.
If you take most React SPAs and do the 5 most impactful things to improve performance, none of those things will require engineering resources your team doesn’t have and none of them will be switching away from React or SPAs. You just have to prioritize doing it. The economics of performance have to work. Etc. The tech stack isn’t the problem.
Ranting on a phone is hard.
If an engineering team, with any amount of resources, doesn’t care about performance, then no technology stack will help. The performance of their app will suck.
Technology stacks can help here. They dictate what is easy and what is hard.
I like your other points - they are practical.
GraphQL requests can always be batched. No matter what UI is going to show up, it only needs one query.
Batching is usually an anti-pattern because:
Batching was needed during the HTTP/1 years. With the adoption of H3, one socket can handle multiple requests.
None of that is true for GraphQL api data.
W.r.t. edge caching, you can rarely cache your db “at the edge” (fwiw I hate that term).
W.r.t. being bounded by the slowest response: you can stream JSON data down. At Meta a component that wants a list of things can request to have it streamed in. The http response is flushed as data is generated. EDIT: I should also add that it’s important you don’t block JS delivery and parsing on data fetching. This is very app specific but if you want a quick primer on best practices google “react render-as-you-fetch.” The tldr is don’t block UI on data fetching and don’t block data fetching on UI.
W.r.t. batching, for example you might have a modal dialogue that is powered by a tree of 60 components and every single one wants to know something about the user. Your GraphQL fragments are collocated with each component and roll up into one query. Fetching and returning the user data once in the query response is much faster than returning the user data 60 times.
Additionally, batching requests allows you to roll them up into compiled and persisted queries. When a person hovers that button and the client queries the server, Relay only needs to send the id of the query to the backend. This helps with reducing bytes over the wire, the client doesn’t need to build a complex query, and your backend GraphQL server doesn’t have to respond to arbitrary queries which can help with ddos. Batching graphql has huge advantages.
you can rarely cache your db “at the edge”
We cache GraphQL requests at the edge all of the time. It saves a lot of time and money. It has nothing to do with a database.
you can stream JSON data down
If graphql clients can parse a partial JSON response and hydrate a component, then I agree this solves it. However, I am pretty sure clients like apollo client wait for the entire response before parsing. In this latter case, streaming doesn’t do much if one query is taking significantly longer than the other queries. Maybe you make sure your batched queries have relatively uniform response times. I have had to resolve many issues where this was not the case.
batching requests allows you to roll them up into compiled and persisted queries
You can compile and persist queries without batching too. If you do it with batching, you had better make sure your batches are consistent. I have seen architectures batch on demand which actually prevents the compile + persist benefits.
fwiw Relay actually also supports inverting things and you can defer a portion of the graphql response, making it asynchronously sent down later. Useful with React Suspense.
But we’re really in the weeds of specific use cases. The DEFAULT for anyone that cares about performance should be batched, persisted graphql queries (that start loading in the initial document GET) for anything rendering on page load. Full stop. It’s not an anti-pattern. Anything else is almost always much slower.
This is well written and I agree with a lot of it but I’m certain the author doesn’t understand the benefits of hooks and it colored a lot of the content.
I’m in the firm “composability over inheritance” camp and I think sharing behavior via functions is a drastic improvement over class inheritance.
Best practice class-component composition was done with higher-order components, not class inheritance, and it was a nightmare. So yes, hooks are easier to compose.
But the things that hooks made 99% better were: incorrect reactivity (eg only a small fraction of class components correctly reacted to their props) and side effect cleanup. I don’t think most React developers realized how buggy their components often were. Link me to any non-trivial React component’s source and I bet I can point at such a bug.
Unfortunately hooks are much harder to understand and read, and they have too many footguns. I don’t think they’re the final api. But they are a huge improvement over class components because they are much more bug free.
What do you think about the ‘signal’ approach described later on in the article?
(Also is it just me or are signals just functional reactive streams…)
Angular, which is listed in the signal group, is based on RxJS, which is pretty explicitly FRP-based. The state management libraries we end up with in the ng ecosystem are mostly “just” organization tools and QoL improvements over using the observables already provided - see https://ngrx.io/ and https://opensource.salesforce.com/akita/
It all depends on the specifics, and I don’t know Solid’s implementation well enough to have an opinion. KnockoutJS had the best implementation of this type of data binding a decade ago and no one has really caught up to my knowledge.
My current work codebase uses jotai which is in the recoil family of data management and I’ve come to loathe it. Jotai atoms look similar to these signals (automatic dependency tracking, computed/derived values, can be used anywhere in the tree). shrug
I honestly just don’t get the Recoil family of state management. It has exactly one real benefit over React context: you can subscribe one component to one piece of data. With React context, if you read anything out of the context, you rerender on any change to anything in the context, even what you didn’t read. That is why Recoil was invented. It’s the only reason to use it (until React fixes the performance issue and let’s you subscribe to a subset of context).
But I think most people reach for recoil/jotai because they have a visceral aversion to passing props through components and would rather just reach for global state. But by defaulting state to global state you’re defaulting your components to being singletons.
As for jotai specifically: the docs/API suck (tell me if any of this makes sense to you: https://jotai.org/docs/utilities/family) and I’ve hit state-tearing bugs which is a huge red flag.
I’ve worked in a codebase that used Recoil and Relay and the Recoil just made all the Relay unidiomatic and impossible to scale.
And we’ll have much faster hardware,
That’s one of my biggest problem with the cloud providers. The hardware you get is so limited, you get so much more CPU and ram on a real machine. My ideal is still renting hardware: you don’t have to spend nights in the datacenter hanging boxes, but you do get powerful machines.
Whenever I look at the pricing for cloud stuff, it’s the storage that I find really difficult to swallow. I guess the reason is that it’s more difficult to move and impossible to over-provision but it’s still incredibly expensive.
You can buy a (reasonably good) 1 TB SSD for $80 right now, which is around $0.08/GB (and that’s not even the best $/GB). A place like DO will sell you storage at $0.10/GB per month, and there are providers which charge even more. Now, I know this is managed block storage, and you’re paying for the ability to scale your disk without copying everything over, but that’s a huge premium.
Yeah, at 100 times more expensive per month, even when factoring redundancy (let’s says x3) and multi-location (let’s say x5), it’s still 6 times as expensive, per month. The main cost I cannot easily compute is the host for the SSDs since they need to be attached to the same machine but at such prices, either someone could come and plug the SSD, or they could actually pay a huge machine with only the storage costs.
It seems hey had S3 (not the same performance as local SSD) at something like 1M for 8PB, which means each terabyte would cost 125k. It’s more reasonable at only 10 times more expensive per year than a raw disk. I’m not sure low volumes pricing for S3 is the same however.
Power to run things costs too. My (extremely generous to DO) calculations suggest each terabyte SSD could cost them $5 / mo to run (of the $100 / mo they bill it out at).
For me it’s egress costs. It’s insane. Quoting myself from the bird site:
“Reminder that for an on-demand t4g.nano instance on AWS, the cost of 3TB/month of egress traffic to the internet is 100x the cost of the compute”
~$2.5 for compute, $250 for egress.
You’re not even kidding. That’s incredible. Absolutely bonkers. A datacenter near me offers a 10gbps unmetered upgrade for $200/mo, which I know is on the cheap end, but still.
And 1Mbps billed at the 95th percentile should be around 1€/month now (at most). For 3TB you’d need 10Mbps, which means around 10€ per month. Bandwidth in Australia seems much more expensive (20x), maybe at 200€ per month. But I’m sure AWS already bills Australian bandwidth at Australia prices.
I’m seeing other figures which put costs of 100Mbps at 50 GBP and 1Gbps at 300 GBP. I doubt Amazon still counts in Mbps.
The big cloud providers offer instances up to around 64 cores with over 1 TiB of RAM (and up to 4 high end GPUs). You can buy faster machines, but few people do (or need to).
But all the old WATs like
["10", "10", "10"].map(parseInt)
are still there.
Could someone explain this to me? What the holy hell happens to make it produce the crazy result [10, NaN, 2]
⁉️
I had never seen this before and just had to try typing it in to see what happened … I guess I don’t know JS as well as I thought I did.
I think map passes the value AND index of the array element to the mapper function, so you get the result of “10” in base 0 (defaults to base 10), base 1 (error because 1 is not a valid digit in base 1), and base 2 (which is 2).
In general in JS, if you see [].map(funcName)
, it’s going to bite you in the ass, and you should change it to [].map(x=>funcName(x))
as soon as possible. And do it even faster if it involves this
.
I understand why, but this shouldn’t be necessary though because it’s verbose and doesn’t meet expectations coming from many languages–introducing a point and needing arguments to get around what sometimes feels like a bug. This is why you’ll see separate functions like indexedMap
, mapWithIndex
, mapi
, and the like in other languages so you’re not overloading the function arity.
Is there are Clippy-like linter for JS/TS that could warn against mistakes like this one? Deno itself has lint
, but that doesn’t seem to catch this one. Nor does eslint
apparently.
Neither do TS/flow. And there’s a reason they don’t: https://github.com/microsoft/TypeScript/wiki/FAQ#why-are-functions-with-fewer-parameters-assignable-to-functions-that-take-more-parameters
It’s unfortunate but there’s not much to be done here. You’d have to craft a very strict, one-off eslint rule to prevent this error in your codebase at the cost of some performance and convenience (eg prevent passing anything but an arrow function to Array.map)
An in-depth description of the problem is here, for anyone having trouble following along: https://jakearchibald.com/2021/function-callback-risks/
Linters cannot rule out cases when the argument function is actually supposed to take an element and its index. But at least they could warn about a possible gotcha.
this is typically why I favour lodash/fp versions as they are strictly limited to one argument for easy composition!
Love letters like this make me think I should give JS another shot. Warnings like that remind me that I don’t want to. It’s an abusive relationship.
See I disagree here - it’s very much a “know the APIs you’re using”.
It is reasonable - and can be useful - for map() to pass things like the index as argument to the mapped function.
It is reasonable - and absolutely useful - for parseInt to take a base argument.
What we’re seeing here is when you try to use these two together.
Imagine a statically typed language:
int parseInt(string, base = 10);
class Array<A> {
Array<B> map<B>(B(*f)(A)) {
Array<B> result = [];
for (int i = 0; i < length; i++) {
result.append(f(this[i]))
}
return result
}
Array<B> map<B>(B(*f)(A, int))
Array<B> result = [];
for (int i = 0; i < length; i++) {
result.append(f(this[i], i))
}
return result
}
};
here if you did [“10”, “10”, “10”].map(parseInt) you would call the second map, and so would get [parseInt(“10”, 0), parseInt(“10”, 1), parseInt(“10”, 2)]
Of course, if you know you know, but for non-JS programmers this is very surprising because:
map
passes multiple argumentsI get that it’s not the common map api in other languages, but coming in with “this is the API in some other unrelated language, therefore this is WAT” is simply not reasonable.
For the latter: not needing exact parameter count is a fairly fundamental part of the language. Saying it’s surprising that map(parseInt) invokes the same behavior as anywhere else in the language is “surprising” is again not reasonable.
JS has quirks, but labeling basic language semantics as WAT is tiresome.
I get that it’s not the common map api in other languages, but coming in with “this is the API in some other unrelated language, therefore this is WAT” is simply not reasonable.
It is still a reasonable criticism because map
exists in a broader social context with widely known conventions. The criticism is that JS doesn’t obey what people would expect. And the resulting confusion is the empirical proof of the problem.
In some cases, you might say, “Well ok, we know people will expect such and such, but such and such is actually a poor design because of X, so we’re going to fix it even if it’s going to cause some confusion”. That is, you can break with convention if you have a good reason. In the case of JS, this defense doesn’t hold up imo.
Ramda’s solution to the problem, which is to add indexing only on explicit request, results in a cleaner, more composable API. Generously, you could argue that “just add i
to every function” has practical convenience, despite wtfs like map(parseInt)
. But that is a debatable counterargument to the criticism. The criticism is at minimum reasonable, even if you don’t think it’s correct.
Also, the fact that map()
takes a function that expects 3 args (IIRC) but is commonly passed a function with one argument and things don’t blow up (neither at runtime nor at compile time in TypeScript) is pretty strange and probably the cause of a lot of these kinds of bugs. In general, JS/TS’s approach to “undefined” is difficult for me to intuit about and I’ve been using JS on and off for about 15 years.
map passes multiple arguments
It’s vaguely reasonable to not know this, but also the signature of map’s functor is on the first line or two of any documentation.
it’s not an error to call a function with the wrong number of arguments
Because variadic arguments (and the arguments array) are first class language features. This is where we get into “this is a core JS language feature, you really need to know it exists”
Variadic functions are fine, the confusing thing is that all functions can receive 0-N args instead of only those that opt in. Yes this has been in JS from the start, but it’s unusual in casual JS use and in other popular languages.
It is reasonable - and can be useful - for map() to pass things like the index as argument to the mapped function.
It is not. You shouldn’t concern yourself with indices at all if you’re using map
et al, and if you need that index, just zip the collection with its indices before mapping.
Or you can remember that other languages have other idioms, and that the index can be useful in principle. There’s also an additional parameter which is the array being enumerated. Together these can be useful.
You my have a functional language puritanical belief about what the API should be, but that’s a subjective opinion, hence it is reasonable that the functor receive additional information about the iteration, especially in a language like JS where variadic functions are first class.
I have the utmost respect for different languages having different idioms (I’ve used quite a lot of OSes and languages throughout the years and am myself bothered when people raised on a blend of Windows, Linux and C-style languages complain that things are just different to what they are used to.) but in this case, it’s just a fractal of bad design.
It’s not just because the map
API is misleading, it’s because the extra arguments are thoroughly superfluous and tries to fit imperative idioms into a functional one.
But first and foremost, it’s a bad design because it never took into account how it would match the default parameter convention of JS.
One could also remark that parseInt
is a strange global function with a strange name in an object-oriented language with some FP features.
You are right. These are totally reasonable APIs. parseInt in particular is totally fine. For map, it’s a little quirk that’s different than maps in other languages, but it’s forgivable because it is useful.
It really bothers me because of how Javascript functions are called. As roryokane says, you can get around the API by defining an extra function that only takes one argument:
[“10”, “10”, “10”].map(n => parseInt(n))
And that works because Javascript functions will ignore extra arguments instead of throwing an error. Ultimately, it’s that looseness that bothers me.
You’re complaining about dynamic typing then, not JS.
Plenty of languages have varargs, plenty of languages have default parameters. The reason map(parseInt) works is because there is no type checking beyond “can be called”.
If you do want “arguments must match exactly”, then all your common calls to map would need function wrappers, because they’d trigger errors from not handling all three arguments.
You’re complaining about dynamic typing then, not JS.
No, I’m not. Python is dynamically typed, but it will throw an error if you provide an extra argument. Python also has varargs, but it’s opt in which I find reasonable. I want dynamic checks at run time, which this behavior doesn’t give me. (Or checks at compile time. Mostly I want to know if I screwed up.)
Right, but parseInt taking an optional base argument is reasonable, and that makes the arity match; python wouldn’t raise an error there.
Again, parseInt is fine. Default arguments are fine. I don’t like how you can pass extra arguments to a function and they get ignored. I want them to throw an error.
There are a bunch of problems. First variadic functions are a first class and relatively fundamental concept in JS, so you’ve got a different language style. That’s just a basic language choice - you can disagree with it, but given that fact you can’t then say it’s unreasonable when variadic nature of calls comes into play.
Second, I could make a JS style map() function, and pass python’s int() function to it, and it would be called just as JS’s is, because the additional parameter is optional, and hence would be fine. The only difference is that python bounds checks the base and throws an exception.
The issue here is that variadic functions are core to JS from day 1. We’ve generally decided that that is “bad” nowadays, but given the functions have always been variadic in JS, large swathes of APIs are designed around it.
Yes, that’s why I try to avoid JavaScript. There are lots of decisions that were made that seemed reasonable in the past, but turned out to have negative consequences that persist. I’ve been burned by some of them and have a negative opinion of the language.
This is just not a WAT. What it is, is a classic example of people pretending reasonable behaviour and acting as though it is WAT. If you pass a function that takes multiple parameters (parseInt is in modern syntax: function parseInt(string, base=10)), and passing it to another function that will call it with multiple parameters, and then acting like the result is a weird behavior in the language, rather than a normal outcome that would occur in any language.
Start with:
function parseInt(string, base=10) -> int; // which is a fairly standard idiom across many languages for defining this function
and then say “map passes the value and the index to the mapping function”, and ask what the outcome would be.
What we’re seeing here is a person saying “I am blindly assuming the same map API as other languages”, and then acting like the API not being identical is somehow a language WAT. It’s right up with the WATs claiming “’{}’ is undefined and ‘+{}’ is NaN”, or “typeof NaN == ‘number’”, etc
Then again, your reply is a classic example of a language afficiando taking a clear WAT and explaining it as reasonable behaviour – even though it quite clearly is a bit of a problem for users of the standard map function coming from practically every other language.
Granted, like almost every other WAT, it’s a rather minor one for anyone who’s been writing JS/TS for more than a week. Still, I’d very much like a linter to automatically catch these for me. Even experienced people make dumb mistakes sometimes, and it’s just a complete waste of time and energy. Rust, a much stronger typed language (whose type checker already wouldn’t allow this), contains a linter that does catch problems similar to this one.
Then again, your reply is a classic example of a language afficiando taking a clear WAT and explaining it as reasonable behaviour – even though it quite clearly is a bit of a problem for users of the standard map function coming from practically every other language.
I’m unsure what to say here. map in JS passes the index of the iteration, and the array being iterated. That is what it does. If you pass any function to map it will get those parameters. If the function you pass has default parameters, those will be filled out.
Similarly, parseInt takes an optional base that defaults to 10.
I get that people are quite reasonably tempted to do map(parseInt), but I cannot work out what JS is meant to do here different, unless people are saying map should explicitly check for parseInt being passed in?
I suppose the simplest way to fix this would for map’s type to be like it is in Haskell, i.e.
(a -> b) -> [a] -> [b]
So the function map takes as argument takes just one argument, and map does not pass an index of the iteration.
I don’t doubt that there are valid reasons why this would be cumbersome in Javascript/Typescript, but I’m not familiar with them. Index of the iteration is not typically needed in functional style, so it’s kind of redundant and as shown here, potentially harmful.
Start with:
function parseInt(string, base=10) -> int; // which is a fairly standard idiom across many languages for defining this function
and then say “map passes the value and the index to the mapping function”, and ask what the outcome would be.
Alas, that’s backwards. Things are surprising not when they are inexplicable in hindsight, but when they are not expected in advance. You have to start from what the user sees, then follow the network of common associations and heuristics to identify likely conclusions.
If the common associations lead to the correct conclusion, the interface is probably fine. If common associations lead to the wrong conclusion, there will be many mistakes — even by users who also know the correct rules.
So in our case, the user starts by seeing ths:
[10, 10, 10].map(parseInt)
Now the user has to remember relevant facts about map
and parseInt
and come to the correct conclusion. Here’s a list of common associations that lead to the wrong answer.
parseInt(mystring)
to mean parseInt(mystring, 10)
parseInt(mystring)
without needing to remember parseInt(mystring, base)
existsf
is simply another way to write x => f(x)
Alas, that’s backwards. Things are surprising not when they are inexplicable in hindsight, but when they are not expected in advance. You have to start from what the user sees, then follow the network of common associations and heuristics to identify likely conclusions.
parseInt takes a base argument. map provides the base. the semantics of both are reasonable. The same thing would happen in any other language.
Now the user has to remember relevant facts about map and parseInt and come to the correct conclusion.
Yes. the user should know the APIs that they’re using. The problem here is that many modern languages have a version of map that has a different API, and end users blindly assume that is the case.
Array.map() provides a third argument. Other languages would blow up when parseInt()
was called with two arguments. You seem to argue elsewhere that JavaScript’s parseInt() accepts more than 2 arguments (because all functions in JavaScript accept ~arbitrarily large numbers of parameters), but that’s precisely what’s bizarre (and as many contend, error prone) about JavaScript–that we can call a function whose signature is function(a, b)
with 3 arguments and it doesn’t blow up.
The issue isn’t that people don’t understand map()
‘s signature, it’s that JavaScript is unusually permissive about function calls which makes it easy (even for people who understand that JavaScript is unusually permissive about function calls) to make mistakes where they wouldn’t in other languages.
No, you want the over application to work, because that’s how JS works, hence how APIs are designed. The alternative is that you would have to do (for example)
someArray.map((value,a,b)=>value*2)
I get that people don’t like JS functions being variadic, but they are, and that means APIs are designed around that variadic nature.
I mean, the obvious alternative is to make map()’s functor accept one argument, but that’s beside the point. We were debating about whether Javascript was uniquely confusing and the answer is “yes, Javascript functions are (uniquely) variadic by default, and this seems to result in many bugs and WATs”. To add to the confusion, it also has a syntax for expressing that the programmer intends for the function to be variadic: function(…args).
If you pass a function that takes multiple parameters (parseInt is in modern syntax: function parseInt(string, base=10)), and passing it to another function that will call it with multiple parameters, and then acting like the result is a weird behavior in the language, rather than a normal outcome that would occur in any language.
It seems like the Go compiler disagrees with you:
func CallWithThreeArgs(f func(int, int, int)) {
f(0, 0, 0)
}
func TakesTwoArgs(int, int) {}
func main() {
// cannot use TakesTwoArgs (value of type func(int, int)) as func(int, int, int) value in argument to CallWithThreeArgs
CallWithThreeArgs(TakesTwoArgs)
}
https://go.dev/play/p/BY7rRRqRMwt
As does the Rust compiler:
fn call_with_three_args(f: fn(i64, i64, i64)) {}
fn takes_two_args(a: i64, b: i64) {}
fn main() {
// note: expected fn pointer `fn(i64, i64, i64)`
// found fn item `fn(i64, i64) {takes_two_args}`
call_with_three_args(takes_two_args);
}
Here’s Python:
def call_with_three_args(f):
f(0, 0, 0)
def takes_two_args(a, b):
pass
// TypeError: takes_two_args() takes exactly 2 arguments (3 given) on line 2 in main.py
call_with_three_args(takes_two_args)
You’re right, calling a function with an incorrect number of parameters does fail. So let’s try what I said: calling a function assuming one API, when it has another. It’s a bit harder in statically typed languages because (A)->X and (A,B=default value)->X are actually different types. In C++
The problem is you’ve misunderstood the claim. The claim isn’t “I can pass a function that takes N and expect it to be called with M arguments” it is “I can pass a function that takes a variable number of arguments, and that will work”. Referencing rust isn’t relevant as it has no concept of variadic functions or pseudo variadic with default parameters. C++ does, and I can make that happen with [](std::string, int = 10){...}
which could be passed to a map the passes an index or one that does not, and both will work.
Your example of python however is simply wrong. If python’s map passed the index:
def map(array, fn):
result = []
for i in range(0, len(array)):
result.append(fn(array[i], i))
return result
you could call that map function, passing int() as the functor, and it would be called. The only reason it would fail is int() bounds checks the base. The behaviour and language semantics are the same as in JS.
Now you can argue about details of the map() API, but the reality is the JS is a language that allows variadic calling, so it is a reasonable choice to provide those additional parameters, You can argue much more reasonably that parseInt should be bounds checking the base and throwing an exception, but again the language semantics and behaviour are not weird.
The problem is you’ve misunderstood the claim.
Your claim was that any language would behave as JavaScript does when passing multiple parameters to a multiple parameter function.
I can pass a function that takes a variable number of arguments, and that will work
Ok, so you’re arguing that unlike other languages, all functions in JavaScript take a variable number of arguments which is just rephrasing the criticism that JavaScript is unusual (thus more programmer errors) and contradicting your claim that it’s behaving as any other language would do (e.g., “but again the language semantics and behaviour are not weird”).
Your example of python however is simply wrong. If python’s map passed the index:
Respectfully, you’re mistaken. Array.map()
passes a third parameter to fn
. If we update your Python example accordingly, it blows up as previously discussed:
def map(array, fn):
result = []
for i in range(0, len(array)):
result.append(fn(array[i], i, array)) // TypeError: int() takes at most 2 arguments (3 given)
return result
I’ve never heard the acronym WAT before, but I would call JS’s “map” function a footgun for sure. It’s named after a classic/ubiquitous functional, uh, function dating back to early LISP, and has the same purpose, but passes different parameters. Yow.
(But I have no problem with parseInt, or JS’s parameter passing in this instance. It’s all on map.)
Here’s a detailed answer adapted from a chat message I once sent to a coworker.
Starting from this JavaScript console transcript, in which I’ve split out a strs
variable for clarity:
> const strs = ['10', '10', '10']
> strs.map(parseInt) // gives the wrong result?
[10, NaN, 2]
The correct way to write this is to wrap parseInt
in a single-argument anonymous function:
> strs.map(x => parseInt(x))
[10, 10, 10]
strs.map(parseInt)
gives unexpected results because of three factors:
Array.prototype.map
calls the passed function with multiple arguments:
> strs.map((a, b, c) => [a, b, c])
[
['10', 0, strs],
['10', 1, strs],
['10', 2, strs],
]
The arguments are element, index, array
.
The parseInt
function takes up to two arguments – string, radix
:
> parseInt('10') // parse with inferred radix of 10. That is, '10' in base 10 is…
10
> parseInt('10', 10) // parse with explicit radix of 10
10
When calling a function in JavaScript, any extra arguments are ignored.
> const plusOne = (n) => n + 1
> plusOne(1)
2
> plusOne(1, null, 'hello', {a: 123})
2
Thus, the incorrect version was calling parseInt
like this:
> parseInt('10', 0, strs) // parse with explicitly assumed radix due to `0`
10
> parseInt('10', 1, strs) // parse with invalid radix of 1
NaN
> parseInt('10', 1, strs) // parse with radix 2. That is, '10' in base 2 is…
2
Yeah, uh, much as I love JavaScript, parseInt()
is probably the worst place to look for elegance in the design of its equivalent of a standard library. It doesn’t even necessarily default to a radix of 10.
The world is boring enough as is. Let’s add more whimsy and cuteness through our service and project names.
I find that I have more time for whimsy when I’m not fielding the umpteenth “what was the purpose of omega star again?” request. I’m not at work for cuteness–I’m there to get paid, support my family, and reduce the annoyance of the lives of my coworkers.
There’s also the unfortunate thing about whimsy: team composition changes over time, and what you find funny today may not be funny later to other folks. Consider the issues you might run into with:
fargo
as the name for a service for removing user objectsgestapo
for the auditing systemdallas
as a name for the onboarding service (since everybody does it)kali
as a name for a resource management and quota enforcement engineminiluv
for your customer service back-office suitehyrda2
because hydra
sucks but version 2 needs to coexist for a while yet with itQuetzalcoatl
after a character from that silly anime Kobayashi’s Dragon Maid (bonus points if you are concerned about second-hand cultural appropriation)fido
(or whatever your dog’s name is) for the fetching service might not be so pleasant after the namesake is sunset from production2consumers1queue
for a data ingest worker pool probably isn’t gonna go over well if anybody on staff has any ranks in knowledge (cursed)
And so on and so forth. I’m put in mind of an old article by Rachel Kroll talking about some logging service or format that was named rather blatantly in reference to either a porno or a sexual act–while this might be a source of chuckles for the team at launch, later hires may object.
As an industry we haven’t gotten over the whimsy of blacklists and whitelists, master and slave, female and male connectors–or hell, even the designation of “user”. If we can’t handle those things, what chance do we think we have with names that seem entertaining in the spur of the moment?
If you still doubt me, consider that AWS seems to engage in both conventions. Which is the easier product to identify, Kinesis or Application Load Balancer? Athena or Secrets Manager? IOT Core or Cognito?
~
I’ll grant that for hobby projects, sure, go nuts. I enjoy thematic character names from anime for my home lab. I used to use names from a particularly violent action movie for cluster nodes.
I’ll also grant that for marketing purposes (at least at a project level) it isn’t bad to have a placeholder name sometimes while things are getting hashed out–though those names often stick around and end up serving as inside baseball for folks to flaunt their tenure.
Lastly, I’ll totally grant that if you by design are trying to exclude people, then by all means really just go hog wild. There are all kinds of delightfully problematic names that function like comic sans to filter folks. Just don’t be surprised if you get called on it.
Lastly, I’ll totally grant that if you by design are trying to exclude people, then by all means really just go hog wild. There are all kinds of delightfully problematic names that function like comic sans to filter folks. Just don’t be surprised if you get called on it.
Meh. The problem with this is that doing gratuitously offensive stuff, like deliberately making your presentations harder to look at, also attracts a bunch of people who think being gratuitously offensive is, like, the coolest thing ever. And when those people find a home in the tech world they soon set about being offensive in the wider community.
Having helped moderate a once fairly significant FOSS thing, I’m pretty convinced that the assholery-positive branding is a bad thing for all of us. It breeds a culture of assholery that we all have to live with no matter where we are.
With all of that said, some cute names don’t concern any sensitive subjects, so I feel like you’re tearing down a straw man, or at least the more strawy half of a composite man. At a previous job we created a service called “counter”, which did have a serious job, but we liked to describe it to management—mostly truthfully—as just being for counting things. You know, like, one apple, two apples… I don’t know if this is funny in anyone’s mind but mine, but the name certainly wasn’t chosen to be a useful description.
It is not only about being offensive.
Homebrew seem to be using beer brewing names and metaphors throughout. As someone who doesn’t know brewing beer nothing makes sense there. It feels to me like they’re taking a subject I know something about (packaging software) and deliberately make it obscure by renaming everything.
I’m similarly put off Kubernetes. Why invent a whole new vocabulary to name existing concepts? I don’t care enough about Kubernetes to try deciphering their jargon.
If you take an existing domain and change the names of all the things then anyone wanting to participate has to relearn everything you’ve made up (poorly in most cases.)
It makes interacting with you like speaking in a second language you have little command of.
EDIT: Just pause for a second and imagine reading a codebase where classes, functions and variables all have cute names…
I think cutesy naming has its place, but I agree that sub-naming of cutesy things (e.g. cheese shop, wheels, eggs from Python packaging) is bad. Your cutesy name should just be a pun with a more or less deducible relationship to the thing it does (e.g. Celery feeds RabbitMQ). You can have jokes in the documentation, but no joke nomenclature.
TIL! I worked at a company where we used Celery – but with Redis as the backing store – for years and I never made this Celery/RabbitMQ connection before.
I don’t know that I disagree in general, either with you or with friendlysock’s comment. I was responding to a specific thing in it that I found significant. If you want to talk about naming more broadly, know that—at least from my perspective—people don’t all engage with names in the same way, and the purpose of names is situational, learned (think of mathematician vs programmer attitudes to naming variables), and at least in some cases a tradeoff between the interests of the various different people who will be using the name. So I don’t think it’s very useful to have a blanket opinion.
Indeed I agree with you. I do seem to have grossly misread your comment and replied to it out of context, my apologies.
I find that I have more time for whimsy when I’m not fielding the umpteenth “what was the purpose of omega star again?” request. I’m not at work for cuteness–I’m there to get paid, support my family, and reduce the annoyance of the lives of my coworkers.
So much this, and also:
The joke is not going to hold up. It probably wasn’t the funny the first time except to you and maybe one other person, and it’s certainly not going to stand the test of time. I can count on one hand the number of “jokes” in comments and names I’ve come across that have actually made me laugh.
You might think you are adding more “whimsy” into a drab world… in fact, you are probably inducing eye rolls for the decade of programmers who will have to maintain your work.
I’m put in mind of an old article by Rachel Kroll talking about some logging service or format that was named rather blatantly in reference to either a porno or a sexual act–while this might be a source of chuckles for the team at launch, later hires may object.
As an industry we haven’t gotten over the whimsy of blacklists and whitelists, master and slave, female and male connectors–or hell, even the designation of “user”. If we can’t handle those things, what chance do we think we have with names that seem entertaining in the spur of the moment?
Honestly, I think this is very easy to avoid if you have a diverse team and take a minimum amount of care in choosing a name. These examples are valid, but I think they are clearly influenced by the insularity and whiteness of software developers.
I’ve built a series of services over the past few years, and my preference has been animal names.
They were not chosen at random, they were designed to be mnemonic.
While I take your point about AWS, Google does the opposite approach and names everything generically, which makes searching for them a pain in the ass. Also, I think there are distinctly different tradeoffs involved in choosing internal names vs external product names.
I think this is very easy to avoid if you have a diverse team and take a minimum amount of care in choosing a name.
As a practical matter, early-stage startups and small companies do not tend to have diverse teams. Remember, naming is an issue that happens with a team size of 1 and which impacts a team size of…however many future selves or people work on a codebase.
You use Coyote
as a harmless example (because ACME like in the coyote and roadrunner cartoons, right?) but similarly banal things pulled from the cartoons like gonzales
for a SPDY library in this day and age cannot be guaranteed to be inoffensive. Even if you take care for today’s attitudes there is no guarantee of tomorrow.
On a long enough timeline, anything not strictly descriptive becomes problematic to somebody (and if you don’t like where the industry is heading in that regard, well, that ship has sailed).
As a practical matter, early-stage startups and small companies do not tend to have diverse teams.
The less diverse your team is, the more care you should take.
You use Coyote as a harmless example (because ACME like in the coyote and roadrunner cartoons, right?) but similarly banal things pulled from the cartoons like gonzales for a SPDY library in this day and age cannot be guaranteed to be inoffensive.
I think this only buttresses my point: Gonzalez is clearly a racial/ethnic stereotype. I would never even think of choosing that. This is not rocket science!
On a long enough timeline, anything not strictly descriptive becomes problematic to somebody
Whereas using a purely descriptive name is much more likely to become problematic on a shorter timeline for the reasons stated in the article.
According to Wikipedia, even though Speedy Gonzales is clearly a problematic ethnic stereotype, there has been a thing where the Mexican community has decided they like him, so he’s been uncanceled. Who can predict these things? https://en.wikipedia.org/wiki/Speedy_Gonzales#Concern_about_stereotypes
You use
Coyote
as a harmless example (because ACME like in the coyote and roadrunner cartoons, right?) but similarly banal things pulled from the cartoons likegonzales
for a SPDY library in this day and age cannot be guaranteed to be inoffensive.
Gonzales was once taken off the air out of racial concerns. Hispanic groups campaigned against this decision because Speedy was an a cultural icon and a hero. Eventually he went back on air
Assuming a person or demographics’s view point is it’s own danger. I am not innocent in this regard
I’m well aware of the history there, never fear. That’s why I used him as an example: using that name may annoy well-meaning people, changing that name may annoy somebody with that Hispanic background.
If we’d gone with the boring utilitarian name of spdy-client
, instead of being cutesy, we could’ve sidestepped the issue altogether.
Assuming a person or demographics’s view point is it’s own danger.
Sadly, that is not the direction well-meaning people influencing our industry have taken. So, in the meantime, I suggest boring anodyne utilitarian names until the dust settles.
Sadly, that is not the direction well-meaning people influencing our industry have taken. So, in the meantime, I suggest boring anodyne utilitarian names until the dust settles. That’s a good point
Also sorry for my wording and tone. I’m not happy about how I wrote that
As an industry we haven’t gotten over the whimsy of blacklists and whitelists, master and slave, female and male connectors–or hell, even the designation of “user”.
I’d argue that such metaphors have always been viewed as more on the descriptive side of the spectrum than whimsical or cute. In fact the idea that these terms are descriptive and in some sense “objective” is the most common defense I’ve seen of keeping such terms around, not that people enjoy them. I didn’t have to have anyone explain to me the meaning of master/slave replication when I first heard that term, the meaning is very intuitive. That’s an indictment of our culture and origins, not of metaphor.
My point is not that cute names are always great, or that it’s easy to avoid their pitfalls. It’s not. But I think holding space to be playful with language is often more generative than trying to be dry and descriptive. And it’s when we think we’re at our most objective that our subjectivity can lead us furthest astray.
I think you’re completely missing that every product and open source project that you simply use will have such a name (maybe except OBS), so why is it different for local homegrown stuff?
For the same reason that brand names can be “Amazon” or “Apple” but we don’t start renaming “computers” or “shopping websites” to something else. OSS projects exist in the same space as “brands” – typically a competitive space in which there are multiple competing solutions to the same problem. In that context, differentiating serves a purpose. However, it also has a cost: what your product does needs to be taught and remembered.
It’s possible that at a very large company some of those same forces take effect, but at small and medium companies they don’t. There, following the same principle would be like insisting everyone in the house call the kitchen sink “waterfall”. Why would I do that? “kitchen sink” works perfectly.
I dunno, left-pad
is pretty self-explanatory, as are sqlite
and kernel-based virtual machine
and logstash_json
and open asset importer
and lib_png
.
There are people using clear, jargon-free naming conventions.
So I guess we slightly differ on what cute and descriptive mean. sqlite has sql in it, descriptive, but -lite gives it a double meaning and makes it unique.
logstash_json is a horrible name, imho - because you could theoretically do the same project in many languages, it could be for logstash itself, or some intermediary product. (I don’t remember the specific one in question). Also libpng is very old and the assumption would be “this is the most popular PNG image library, written in C”, maybe because of the (weakly held) naming convention. These days we get many more new projects per year, but in theory a PNG lib in Erlang/Ruby/Brainfuck could also be called libpng, it’s just that the name was taken.
Maybe I am completely wrong here, but I understood the OP’s post more as “don’t use bland descriptive, pidgeonholed names” and you argue more over “don’t do cute names” - so maybe it’s a middle ground.
And yes, I still remember when a coworker wanted to call 2 projects “Red Baby” and “Blue Frog” or something and no one had any clue why, he couldn’t explain it, and we said: Why would one be red and one be blue?
logstash_json
has the project slug “Formats logs as JSON, forwards to Logstash via TCP, or to console.”. That’s roughly what I’d expect from the name, something to do with Logstash and something to do with JSON.
libpng
is…“libpng is the official PNG reference library.” Sure, a slightly better name would’ve been “png_ref_implementation” or whatever, but again, the name tells me what to expect.
sqlite
“implements a small, fast, self-contained, high-reliability, full-featured, SQL database engine. “ So, you know, a SQL thingy but not large. Light, if you would. Again, maybe the name could mention the standalone nature, but that’s neither here nor there.
I think that bland, descriptive names are in fact exactly the right answer.
Again, examples from the other side:
nokogiri
is a library for Japanese saws…no, wait, it’s for parsing XML and HTML.
Angular
is a library for angles…no, wait, it’s an HTML templating and data binding library (among other things).
Beautiful soup
is a library about soup…no, wait, another HTML/XML munging library.
Ristretto
is a library for short shots of espresso…no, wait, it’s a caching library.
kubernetes
is some tool for pilots…wait, no, it’s container orchestration and provisioning.
Prometheus
is tool for giving fire to mortals…wait, crap, it’s a time series DB and monitoring setup.
These names do not enhance understanding.
Having no centralized server looks very appealing for a mesh overlay network in a home environment. Any Yggdrasil reviews? The main two things I’m curious about are:
Re: security: from what I know, Yggdrasil IP addresses are derived from the public part of the keypair used to connect to the network - in order to use the same IP address as you, someone would basically have to crack your private key or find another private key that corresponds to a public key that becomes the same IP address.
I must be missing something still. People can use whatever ip address they want. Are you saying that people can use whatever ip, but a Yggdrasil node won’t communicate beyond some initial authentication step if you can’t prove you have the private key for that ip? Is every udp packet unwrapped to do that Auth?
Every packet is encrypted to the target’s public key, so a TCP connection can’t complete a handshake if one of the peers doesn’t have the private key to decrypt the packet’s contents. I’m not sure, but I wouldn’t be surprised if the packets are signed by the source’s private key so that the target can verify packets are coming from the source, making UDP secure.
Couple of useful extensions:
Things I still miss:
I don’t have tabs 🤣🤣🤣
But yeah, Toggle Editor Group Sizes is close to what I want, thanks a bunch! Is there a way to make it work with explorer as well? So that it automatically shrinks/expands based on whether it is focused?
I’m glad it was useful!
Hmmm, I don’t know. But you can go into zen mode where it auto hides, and then you can make it show up while you interact with it and then it auto hides when you give focus back to the buffer?
If you can deal with google ads every fucking paragraph this might be worth a read before installing: https://linuxiac.com/vanilla-os-promising-idea-disappointing-implementation/
I should probably stop going there but I think this article is representative of a certain… fracture within our industry, specifically, a fracture about how the relative importance of design elemenets is perceived. It’s based on the idea that this inconsistency is somehow super important. I think it’s not. And it misses important points about functionality, because it’s written from this perspective that UX and functionality are evolved separately, and on separate tracks, but the practical, everyday reality of writing and maintaing software should’ve long taught us that’s not the case.
First of all: this is 2022, half our tools are web-based. Even if Microsoft were to dump all the pre-Windows 11 era UIs from their system, that would barely make a dent in the consistency of everyone’s interfaces. Not to mention everyone using CAD and gardening apps from the nineties and 00s and whatever in addition to web tools. Pretty much everyone around the world goes on to productively use terribly inconsistent interfaces. I think that, more than, what, fifteen years into the web app revolution, it’s safe to admit the uncomfortable truth we were all afraid to admit about inconsistent UIs back when they became popular: nobody cares about that (for statistically relevant values of nobody, of course: I want the Workbench UI back, and I got friends who want the NeXT UI back, but we’re nerds).
Second, grab your coffee, I’m gonna tell you a boring story. A long time ago, when Windows 95 came out, you could still install the old Windows 3.1 Program Manager shell on it. You could even choose between the two when you installed Windows. By Windows 98, you had to do a registry hack, but the Program Manager was still there. It was finally removed in the days of XP.
Why did the world of computing not go up in flames because Windows offered two shells? Because the new one was good enough that almost nobody wanted the old one. Even in the days of Windows 95, this was mostly a curiosity – some IT departments still rolled out the Program Manager interface, concerned that their less technical staff wouldn’t like the change, but as soon as they saw the taskbar and application icons, they wanted those. Early Windows 95 books (yep, we had those, too) showed both; by the time Rachel began working at Bloomingdale, it was a footnote.
The fact that Microsoft is still putting out UIs at this point isn’t just proof of how messed up the corporate world is: it’s also proof that the new ones just aren’t good enough to make people want to dump the old ones.
So – third, I guess? – I actually like this. One of my work machines runs Windows 11. I’m glad that this is how it works. I don’t care that the Run dialog looks weird, what I care about is that it’s still there, so that I can still use it instead of the hilariously disfunctional Search feature from the start menu. The fact that it hasn’t been removed doesn’t (just) mean Microsoft sucks at UI design – it means they care about (for corporate definitions of “care about”, i.e. they care about the money they get from) the people who still use their old design. If the new one were that good, there wouldn’t be any of those. But it’s not, so there are.
Still allowing people to use the old design, if they’re aware of it, is actually the user-friendly choice here. “Upgrading” me to search system which, because I’ve installed Zotero, insists on autocompleting anything that starts with “intern” to “International Nuclear Information System” instead of Edge, which I use every day (and which we all know why it was offered, seeing how Edge doesn’t start with “intern”…) would’ve been the unfriendly choice here.
And fourth – and, for me, the most important lesson: the Device Manager looks ancient, but the real value in such an ancient program isn’t in muscle program, it’s in reliability.
The Device Manager looks the same as it did twenty years ago. In twenty years, Ubuntu went through like four or five clones of a device manager-like application. Maybe if they’d kept one around for long enough, they would’ve ended up with one that worked well. But they didn’t, and instead of an ancient-looking app that does a bunch of things well, we just got a series of half a dozen fresh-looking apps that were either unstable, useless, or both.
And this hides a bitter piece of irony: we like to shit on proprietary vendors for not giving their users a choice about things. But the next time you have to re-learn how to perform whatever ritual you need to perform to not have blurry fonts in Gnome this year, remember that, if you learned how to manage Windows font settings fifteen years ago, the same company that gave us the Halloween documents still allows you to use that knowledge productively in addition to whatever Gen Z uses, whereas we, the people who are always in control of our computers, regularly learn a different dance for how to not squint at GTK apps.
Shit. So close, I’d diligently filed everything correctly under 2023 until yesterday. Now every date I write is going to be in 2022 until April :-(.
I get what you’re saying and mostly agree but the problem is that certain areas of functionality that should be consistent aren’t, eg I still can’t edit my pc settings without invariably ending up in some ancient dialog, because the modern settings are still missing all the options. It’s been 25 million year and they still haven’t ported basic mouse and audio settings to the new UI.
That’s much worse than “Photoshop doesn’t look native.”
the problem is that certain areas of functionality that should be consistent aren’t, eg I still can’t edit my pc settings without invariably ending up in some ancient dialog, because the modern settings are still missing all the options. … That’s much worse than “Photoshop doesn’t look native.”
I don’t mean to criticize you for caring about it, but I, for one, can’t imagine I would see ‘how a settings menu looks’ as a problem at all, let alone one ‘much worse’ than how some other program looks.
Come on, encrypt all the metadata. Why are the website URLs just sitting around? That’s just negligence.
But taking a step back, this is an even older conversation of “why rob banks, because that’s where the money is”. This was a very persistent and targeted attack. Sure, maybe the backups shouldn’t be on a “storage volume” and a blob store would be a better fit to enforce contingent authorization. But once a hosted service is too attractive can any of them resist all attacks? Something for me to ponder, I really do like 1Password.
When using cloud based password managers, people like to say “it depends on your threat model!” But whose threat model is “Eventually I will lose all my PII that I store in this service and I can’t mitigate the threat”? If it’s a question of trade-offs and probabilities, hey you’re more likely to forget strong passwords or use weak passwords than have 1Password or LastPass hacked, firstly I’d say the metadata is as important to protect so their designs matter, secondly this trade-off is unsatisfactory because the sheer attractiveness of the hosted services is tilting the probabilities too far towards the services being compromised.
Does LastPass have a lot more users than 1Password? I ask because they have a lot more security breaches.
I don’t know about the numbers, but I know a large enterprise mandating lastpass as the only pw manager for all employees. They are def. a valuable target.
Content warning
I make a food-related metaphor later in this tutorial.
I don’t get the joke? Can anyone explain?
fwiw @icy I think some individuals are being a tad over the top with their responses… Good job with the tool :) Gets the job done.
I didn’t see a single comment that crossed the line on being hostile but before leaving feedback on a post like this I’d urge you all to weigh two things: (1) the importance your feedback be heard vs (2) that feedback contributing to an environment that would make a person not do such work again in the future (or at least not announce it on this site).
Usually negative, constructive feedback is good but I don’t see how anyone could read this comments section and declare it useful.
I don’t see how anyone could read this comments section and declare it useful.
This, exactly.
Here’s someone who’s shared a hobbyist project they worked on for a week, and what’s the response like?
Like, if you wanted this place to become the orange site, well done all, you’ve done it! its now as obnoxious and as useful.
Just want to flag that Apple is misusing the term “end-to-end” to refer to data storage rather than communication. You could call it client-side encryption with on-device key storage.
End-to-end implying zero-knowledge encryption (where the device encrypts the backup, then decrypts on restore) is accepted terminology. Just like how Signal offers end-to-end encrypted communication and Messenger doesn’t, yet both encrypt over the wire.
EDIT: another way of thinking of this is that “end” in “end-to-end” are the devices meant to utilize the backups (or messages or whatever) and NOT Apple’s (or whomever’s) servers. So over-the-wire encryption is NOT considered “end-to-end” encryption, because the encryption is removed at something in between the ends (Apple’s server).
That’s not what zero-knowledge means either… I don’t think either of those are accepted terms for client-side encryption for remote storage. “End-to-end” implies that there are two ends, and it specifically refers to encrypted communication between two parties.
Unless you mean it’s an “accepted term” in the sense that people who venerate Apple will now accept it because Apple used the term that way. Maybe, but it’s an obstacle to understanding.
Oh you know I just realized the point of confusion: when you said “communication” you didn’t mean over-the-wire encryption, you meant messaging.
Still, even though I don’t like the term, it is in use outside messaging, and not just by apple. I see it used for backup services, note-taking applications, etc. It’s common enough for the Wikipedia entry on end-to-end encryption to have a paragraph about it:
Some encrypted backup and file sharing services provide client-side encryption. The encryption they offer is here not referred to as end-to-end encryption, because the services are not meant for sharing messages between users[further explanation needed]. However, the term “end-to-end encryption” is sometimes incorrectly used to describe client-side encryption.[24]
Trying to limit a term that is just some English words to messaging-only by convention strikes me as inane. If we wanted to limit it to message exchange only we should start by calling it “person-to-person encryption.”
Sounds like that might be a controversial claim though and there’s definitely merit to what you are saying.
It’s not specific to messaging between users, it’s any communication between different entities (the “ends”) who are meant to have exclusive access to the content. Maybe you can argue that encrypted data storage is communication between a user and their future self, and that these are the two different ends, but that seems like an obfuscation to me.
Still, even though I don’t like the term, it is in use outside messaging, and not just by apple. I see it used for backup services, note-taking applications, etc. It’s common enough for the Wikipedia entry on end-to-end encryption to have a paragraph about it:
So misuse of the term “end-to-end” is common enough that Wikipedia notes that the term is sometimes used incorrectly. I have no disagreement with that, my original comment was to point out that this is one such example of the incorrect usage.
I tried using sops but couldn’t understand the docs. I must be daft because I still cannot wrap my head around how it works after reading this article. Nothing explains how the encryption/decryption actually happens.
The host ssh private key? Why would the host have a private ssh key being used for decryption? Do I have to have a copy of that locally when encrypting the yaml files?
As I understand, the basic idea is that you have a file containing encrypted secrets. SOPS is a shim layer between your editor and the encrypted file: it opens and parses the file, decrypts the values, and opens the editor on the decrypted file. When the editor exits, it parses the modified file, encrypts the values, and saves that. (Note how SOPS only supports a specific list of structured file formats.) Based on the SOPS docs, it looks like the tool also supports performing those middleman operations via CLI invocation, which would be how you can build a NixOS module that does those operations on secret files.
The SOPS README has different sections for various secret key formats, like AWS or SSH, and the configuration for those seems to rely on environment variables. So I suspect the “how does *cryption happen” question is answered by which environment variables are set, which indicates to SOPS where to look and what to do with it.
sops supports a few different key formats. Since hosts tend to have ssh daemons on them, using the ssh private key to decrypt its secrets is handy.
Just the public key.