Where Yegge’s Wrong
tbray.org
tbray.org
How is AdWords the channel? It seems like the product to me, while Google Search is the vehicle for delivering them.
>My personal opinion is that Android originally existed to prevent Apple getting a monopoly lock on the hand-held market and lock Google entirely out of mobile ads.
Apple's business model doesn't lend itself to getting a monopoly lock on any business they're in. Apple was always destined to having a nice chunk of the premium market, but someone else would have sprung up to serve everyone else. It could have been Symbian, Blackberry, Microsoft, etc.
If anything, the companies we need to worry about are those who offer extensible "open" platforms that build partnerships with most of the industry, while retaining control and decisionmaking authority over them all.
Android won it's success in the budget market, and that gave it the ecosystem it needed to survive in the high end market.
Even that's misquoting Yegge, he in fact said:
>> Android is probably Google’s most important channel — if not today, then certainly over the next ten years.
https://medium.com/@steve.yegge/who-will-steal-android-from-...
He's not even saying it's a more important channel than search today but something that has the potential to be in the next decade.
Additionally after rereading Yegge's post, I don't see anywhere he asserted AdWords is a channel either, which it clearly isn't. He states channels are merely a proxy for ads or it's "little sister" paid subscriptions.
And when you're speaking of channels, as something entirely distinct from ads/subscriptions, then it's clear they must be things customers want: positive experiences, good content, social gratification, etc. Aka, good products. Users are not just a faceless question mark before + "impressions for ads money = profit". It takes hard work to keep users happy in order to even have the opportunity to show them ads or sell them subs. So you're entirely correct here.
Google is very much in the business of customer happiness, as much as they are in the business of keeping advertisers happy on the other end. They are not merely middlemen for advertisers... they control plenty of channels.
Which I believe is based in a common myth about businesses in general, popular among anti-capitalist political ideologies: that industry is only about "making money" - as if that is somehow distinct from providing value to customers. Absent a central body ala governments or in some cases monopolies, it's impossible to make money without providing value in return, aka keeping 'end-users' happy.
Their primary products are application platforms. How on earth do you reason that their business models don't lend themselves to lockin, when it's the exact same business model that created Microsoft?
They are heavily subsidized by the phone operators in most markets (sure you end up paying at least as much to the operator, but most people only see the upfront cost).
There are always multiple generations on sale at the same time, and lower cost iterations of the current gen as well.
Not to mention refurbished phones.
In the USA, iOS is roughly half of the smartphone landscape. Yeah, the less costly iPhone is more costly than the cheapest Android phone, but I don't think it can be said that it is premium hardware.
Is he being serious? I can't tell if its a joke or not.
Of course he's joking.
Tim Bray wrote this, not Steve Yegge.
Can anyone elaborate on this?
- Followup to the above: because so much of the "work" involved with UI code involves interacting with another API, usage of that API tends to spread itself through the entire codebase like cancer. You can create a careful separation of concerns, but it takes a lot of upfront work, verbose boilerplate code, and constant vigilance -- if you're just handed an existing UI codebase you won't have that luxury.
- UI code is highly stateful and highly mutable. A core skill in UI programming is managing your app's enormous state profile. Single-path, whole-world rendering techniques like React/other shadow DOM techniques really help here.
- UI is highly temporal. It needs to change over time. Testing anything to do with time is an enormous headache. Animations can force you to temporarily separate your underlying state from its representation, which is a testing nightmare. But just in general, writing unit tests that involve time passing is infuriating and fragile (even if you can control time passing via a mocked clock object).
- A lot of core functionality in UI involves different pieces handing off to each other -- one window transitions to another window, etc. To test that properly you need integration tests, but now your integration tests also need to interact with the underlying OS/browser (because you can't separate any of that stuff out). So your integration tests are in general way flakier than you can get on UI-less systems that really only need to worry about faking out a DB of some kind.
Take spacing: if something moves 200px it's probably broken. But a couple pixels off are usually fine. Unless those 2px are the vertical position of an icon inside a button or two things that need to align. Where do you draw the line? Humans are pretty good at spotting when things look off just by looking at a screen. Computers, not so much (yet).
However, after spending some time with it, literally everything else now feels dramatically inferior.
Unit testing frontends is hard because to create a test case, you must simulate a human user's behavior (clicks/taps on a UI) which is inherently complex, and has a lot of possible variations, and then test the correctness of the response the system displays as a human would perceive it. Just because the system sent back the correct data doesn't mean it was rendered in a way that the user can see/understand.
With non-user-facing systems, you can just send the input, and test computationally that the output fits the expected value or range of correct responses.
Of course, one could create a vision system that tested the response of a UI for correctness, but that would be ... super-hard.
It's more a problem that iterating/debugging/testing is generally so much harder for front-end, however you do it and regardless of whether you're dogmatic about unit testing or not.
Focusing on the difficulty of unit testing is simply focusing on one of the symptoms rather than the root cause.
Like in backend code, I can back out of a method, rollback to the start of a call and re-run it all again when debugging. In front-end code, that's simply not possible because there were a bunch of user interactions and you can't re-run them, you have to restart and click, click, click.
First, why do many people think that unit testing UI is difficult? How do you unit test UI? There are a couple of schools of thought about "unit tests". A very prevalent one is that you write a "unit" (usually a class) and then you stress test the interface. Often this is coupled with faking/mocking collaborators because we are only interested in testing the interface.
This is kind of problematic with UI code because imagine that I have a class that is derived from a dialog box. How do I test (for example) that the desired functionality is run when I hit the "OK" button? The problem is that the "OK" button is often not a programmable interface on my dialog box. Someone clicks on the button, whatever UI framework I'm using takes over and then magically some callback (hopefully) get's called. I can (sometimes) hack into the depths of my framework and maybe mock the callback to make sure it gets called when a button is pressed, but usually frameworks are not built to accommodate this kind of work. I can also use some kind of UI simulator to press buttons and that would work OK. But I'm going to back up here and suggest that this is not a unit test. It's an automated regression test.
Part of the problem is the framework is "hiding" the internal functioning of the UI. I can't test the OK button, because I just can't get access to it. For me this is a big hint that "I'm doing it wrong". In fact, I don't subscribe to the common view that "unit testing" is about testing the interfaces on a class -- in other words, black box testing. Unit tests are specifically different than other kinds of test: they are white box tests.
I've described this a few times and have yet to find a way that works particularly well, so forgive me if I fail yet again (you can happily walk away with only the first half of this message :-) ). Imagine that instead of "testing", we simply want to insert "probes" into our code. The probes will measure the operation of the code in a specific place and warn us if something is outside of our expectations.
For example, imagine cooking a roast beef (sorry if you are vegetarian -- you can imagine some other kind of roast). We want to cook the roast to a certain internal temperature. There are many ways to do it. A "black box" approach would be to weigh the roast, measure the temperature in the oven and then time how long we are cooking. We can then derive the probable internal temperature from these external parameters.
The "white box" approach would be to stick a thermal probe (thermometer) into the roast and measure the temperature directly. This allows us to see exactly what's happening on the inside, but at the cost of violating the roast's encapsulation.
My opinion is that "unit testing" is this latter kind of testing. We want to expose the internal state of things and to measure it directly -- rather than deriving what we assume the state to be from some defined interface. I furthermore believe that "TDD" refers to the systematic act of finding appropriate internal state and exposing it. Test first is a good way to do that because you end up probing the state before you have written any interfaces -- it forces you to open up that internal state. There are other ways to do it, though.
All of that to say that UI code is not actually any different than any other code. Our difficulty is not that the events driving the system are generated by a user. Our difficulty is that the frameworks hide the internals and stop you from writing unit tests (by my definition). This happens frequently when you are trying to TDD legacy code. The legacy code was not written in a way that allows you to write unit test -- you are forced to write integration tests because you don't have access to the internals. This is exactly the same with most UI frameworks -- they were not written to allow unit testing. And since you can't (or really don't want to) start refactoring the framework, you are stuck.
Having said that, I have found some frameworks to be amenable to unit testing. For me, the best I've found in the web world is React. Interestingly, though, I do not use the React testing framework because it is not amenable to unit testing (by my definition). I build my own.
Hope you found that interesting!
This has further implications, like over-abstraction (all those dependencies need to be parameterized, all those classes need to be interfaces, everything must be indirected so it can be probed), namespace pollution (instead of a cohesive external API, you get a big bag of Lego pieces and have to pick and choose the right combination), brittle design (every unit boundary gets tested on both sides, tripling the cost of modification), etc.
I'm trying to imagine what you are thinking of, but I'm having trouble so it's hard to rebut your points.
Choose almost any moderately popular jar you like from Maven and I could point out the pathologies. Strong smells when you have lots of interfaces with a single implementation, lots of construction indirected through factories (or factory factories), etc.
Additionally separate your components logic from rendering and test that; e.g. a calendar widget I just created has a load of tested functionality for creating lists of dates with various attributes (isCurrentMonth, isToday, isSelectable, isSelected, etc). This can be unit tested just fine.
Finally I’d strongly recommend cypress for doing e2e testing.
and what -is- the deal with Flutter? Is it Dart 2.0?
The consensus is that there are always big downsides to cross platform solutions and that it is not going to change anytime soon. They have their niche, but currently have zero chances to replace native.
Flutter is a runtime for Android and iOS powering apps written in Dart. Flutter itself is written in c++ and Dart.
The main originality is that it uses it's own runtime instead of using the platform widgets. It is not as original as its creators would like you to believe though since that's basically what all the cross platform toolkit running in a webview has been doing. The difference is that performances are not atrocious with flutter but they still fall short of being any better than native apps.
Flutter could be a big deal if/when Fuchsia becomes a thing it is adopted as its main app runtime. And by 'become a thing' I mean that it completely replaces Android.
You would get native dev on 'android z+' and high quality iOS ports very easily.
Yeah, no, that's hardly the reason.
Besides, it's wrong. It might be harder to functional or integration test properly, but unit-testing front-end code properly is not an issue.
As an aside, it is actually more important to unit test lest "safe" languages. E.g. Java ensures you're passing correct types around at compile time. But with JS/Python et al. you need to unit test to check that you're passing, say a String rather than a tomato.
That is an engineering culture thing. I work on a small JS project (node.js server + React FE) that has >95% unit test code coverage. It isn't even difficult to do - it is just not a major part of a lot of front-end teams culture.
That doesn't mean that C++ programmers aren't looking down on those people, though.
I want a picture of this.
If I never have to read "Unless you're paying, you're not the customer you're the product" again, that shit will be too soon. If they don't make something that people actually want to use, there will be no audience for advertisers. Come on.
Lastly, I thought it was funny that both Yegge and Bray can agree to take shots at JavaScript. There's another thing you'd think nerds would get tired of already.
Why? JavaScript is really, truly, awfully horrible. It's a local maximum which could easily be escaped with just a very little bit of effort, and the fact that no-one has done so is one of the strongest-possible condemnations of our industry. JavaScript is a shame, an embarrassment and a squandered opportunity. When one thinks that we could have had Scheme, and instead thanks to a benighted Netscape executive we're stuck with JavaScript forever — it is to weep.
I jest. ECMAScript is the most widely installed programming platform on the planet. Ever. How do you even begin to shift that?
My understanding is that we do (or did) mostly have Scheme with the first release of JavaScript, just with different syntax. What about Scheme, what features or syntax, would be so much better than JS that we should switch?
Would you elaborate on what makes JavaScript so bad? How much have you used it yourself? Having done lots of C++ in my life, I find writing JavaScript quite a bit more enjoyable. Especially recently now that the dev tools in all the major browsers have gotten so good.
> When one thinks that we could have had Scheme
This may have been true 5 years ago. I'm probably the biggest Scheme fan there is. I've created assemblers, compilers, and even started an OS in Scheme. I've been using Scheme since the late '90s.
Modern ES6 and beyond JavaScript is quite nice. I really don't miss Scheme at all. Scheme, mind you, was incredibly limited in R4RS and R5RS, around the time Netscape would have adopted it anyway. It didn't even have a module system, much like old JavaScript. We would be stuck with the largest problem still facing JS: multiple sucky module systems requiring a webpack-like monstrosity.
Javascripts approach to threading - default single threaded event loop, but with message passing to communicate with other threads is actually a pretty good model for people who want to write multi-threaded code that doesn't break. This is important when coding for a very open platform like the web. Confusion about what is running on the UI thread and what isn't and how to communicate between them is the kind of error that pops up in java ui code. I wouldn't want that for the web.
Besides, the threading approach is more about the runtime rather than the language. There are lots of clustering and threading libraries on npm if you want them for your server-side code, and you have WebWorkers in the browser.
Sure, the implicit type coercion (pretty much everyone hates this) and var args (hasnt bothered me as much as it seems to have bothered you) were not good choices, although compare it to other languages of the time - first class functions were by no means universally available and now almost every modern language has them. A literal notation for maps in the language was a great decision too. Prototype inheritance may be a little weird but it is elegant, more general and radically simpler than class based inheritance.
Javascript certainly has its warts but I find it fairly productive to code in. Plus it's paired with a runtime that has seen a shocking level of genius engineering over the last 10 years. The speed of iteration is unparalleled. The reach of a deployed piece of code is unique and likely to remain so for the foreseeable future. There aren't many other platforms that have developer tools for inspecting a running system, visualizing ui, network and detailed render performance that are as good.
This is simply wrong. The "approach" to threading is that there is none. It's single-threaded. You cannot write multi-threaded code directly in JS. On the server side, this can lead to things like a naive fetch after cache miss denying service to your web application. Nothing on the queue can run until the current function call finishes. Even lovely Python doesn't have this issue because, while the GIL is a PITA, Python has real threads, and can switch during a function call.
To be clear, I'm not talking about web browser programming, where JS for some time has been the only option. I'm talking about the ridiculousness of server-side JS when we know clock speeds are hitting a wall and that parallelism is the only way to squeeze out more performance. Steele, Armstrong et. al. have lectured at length on this topic.
> There are lots of clustering and threading libraries on npm
No, there aren't. Running several node processes via PM2 is not threading. You cannot use multiple cores directly from the same Node program unless you drop down to C++.
You can vaguely do it in the browser, but the limitations on communications between the main thread and web workers, especially with SharedArrayBuffer being off-limits post-Spectre, are pretty much non-starters. It's a nice option to have for specific cases (games programmers take advantage of web workers to read models without blocking the main thread, for instance), but it's only bolted onto web browsers.
Well, someone has to drop down to C++, but it doesn't have to be me (node webworker-threads). This is the same as in most languages.
> you cannot use multiple cores directly from the same Node program unless you drop down to C++.
As I said in my comment, threading is primarily a runtime thing, not a language thing. There are javascript runtimes (napa.js, node webworker-threads, nexusjs, browsers) that have threading (even by your restrictive definition that doesn't include threads layered onto child processes like the npm package 'threads' does), just as there are runtimes for e.g. java that do not (and the bulk of the threading support in most other language runtimes is also written in C++ so it seems churlish of you to complain about that in javascript runtimes). Most languages do not need significant changes to their syntax or keywords to support multithreading.
Maybe you're comparing node.js specifically (where I think that for a long time there was a belief among the leads that letting the OS manage processes was a better approach - it's been interesting to watch this same argument play out in the rust community) with something like Go which has pleasant primitives for concurrency built in as part of the language. If you are, then to some extent I agree with you - something like goroutines might be really nice in javascript and is arguably better than what the js community has, but I also stand by my runtime/language distinction. Goroutines are not OS threads. You can run go programs with GOMAXPROCS set to 1 without changing the language at all.
I'm sort of amazed that the implicit var args don't bother you... with var args promoted to first class syntax in ES6, the only reason they're still there is for backwards compatibility. Passing an unhandled arg to a function should at least be a runtime error, and likewise omitting a positional arg that lacks a default. I've seen countless bugs caused by this "feature" alone. When I think of JS programming in the large, I think of "undefined is not a function" and "cannot read property 'foo' of undefined." It's quite hellish.
I figure, there are 3 possibilities:
(1) you're right, and that Javascript is horrible, and the entire software development industry is lazy in not replacing it.
(2) it's actually very difficult to replace
(3) it's not as bad as you think it is.
I'm not sure which is correct, but I wouldn't expect it to be the one that requires malice or incompetence on the part of an entire industry of people who love replacing thing.
JavaScript delenda est.
To rephrase Yegge’s original question with this lens: is Google focused more on generating revenue from its customers (advertisers) by hooking users into their service ecosystem? Or are they being unwarrantedly distracted by “competitors” of their offerings, when users’ interest in those competitive offerings isn’t actually decreasing their attachment to the Google services ecosystem at all?
See? Very different question.
Your point is great, and important. The situation is more nuanced than customers vs users. Google has multiple types of customers, and both are important. Free customers using search still have to be appeased by search features, and not too irritated by ads, otherwise ad revenue goes away. Ads customers have to reach a large audience, otherwise they go away. Like many companies, internally Google has departments that are in a sort of competition with each other, and the tension has to maintain an important balance. Ads can't be the only customer, and it can't be the only thing Google cares about.
While I'd agree it's getting a bit tired, the context where this aged phrase makes more sense is when discussing privacy. I've seen it most often used to try to get the point across that privacy will never be a goal of free ad-supported internet services. Tired as it is, that point might be worth repeating until the general population and the government actually take it seriously.
BTW, I didn't see any shots at JavaScript here, my take was he was defending it.
It seems like his real point is that Google is not being innovative on the grand scale. Which is fine, but that's distinct from not being user-focused, and distinct again from not being customer-focused. Those are three different problems, and they get solved three different ways.