We’ve built a towering pile of shite
shkspr.mobi
shkspr.mobi
But I have been absolutely bowled over, and inspired, by the Rust tooling. Not only helpful error messages, but hints about misunderstandings you might have had, along with concrete suggestions on how to fix it.
I feel like it corresponds to the highly rigorous thought that has gone into the language, which also results in a great language irrespective of the tooling.
Thank you Rust team.
I'd say it's less about rigorous thought, and more about being willing to spend time on purely user-centric things.
You can have both of course, but surely things like proof assistants have a lot of rigorous thought put into them, yet the error reporting is usually horrendous.
Improvements to Rust's error reporting is the result of a specific and dedicated effort in that direction: https://blog.rust-lang.org/2016/08/10/Shape-of-errors-to-com... (and https://internals.rust-lang.org/t/new-error-format/3438 and https://web.archive.org/web/20160807033841/http://www.jonath...).
And note that the new errors didn't really provide more information (at least initially), instead they formatted the information better, removed redundancies and noise, and called out the relevant bits more clearly. This runs very much opposite of "rigorously" dumping all the stuff you have and letting the user sort it out.
So much so that we have separate technologies to write in Javascript and then build it for Android (PWAs).
The server-client equivalent would be writing one codebase, in one language and building for browser, and server separately. Despite server-client predating Android by decades, that still does not happen.
It's a bit of a rant - but I hate anything that comes with IDE - I face greater cognitive load in undertsanding the IDE than writing logic.
In languages like Python, where you might run into an issue caused by not have implemented an arbitrary method on a callback, or Obj-C where you might have forgotten to implement a delegate, you're on your own as far as the compiler is concerned.
Yesterday my Rust code didn't compile because I was missing a bound on a type parameter on my function that I passed though into the key type of a hashmap. Rust said "hey maybe you want to add this constraint to your declarations".
Some of this you can put down to dynamc vs static. But beyond that, it's clear that the designers and contributors haven't just said "we want to make a language with great safety" but then went on to ask "what specific ergonomic challenges will this bring to users, and how do we help them" .
I don't think you could get those ergonomics from many other languages, even if you wanted, without extra static analysis.
(edit: lazy typing on a phone; in the last para I was referring to the mainstream languages like Python rather than the whole universe of languages.)
You absolutely could. Hell, languages which are much simpler to analyse have worse error reporting, and the language which started the movement of having less shite error message is a fairly simple one: https://elm-lang.org/news/compiler-errors-for-humans
I shudder to think about the next time I have to do npm decency upgrades. Even worse when you have synchronize typescript types. Which requires reading through random GitHub issues to realize that you need to pin a random @types package to 1.20.30 because that’s the one that magically worked for a commenter with 20 thumbs-up reactions.
The amount of signal to noise is low because of low barrier/high user base community spamming NPM packages/and spamming ratings/stars/dependencies - it's hard to determine what's "safe" to use, and because the environment is so barebones you need to depend on a lot of third party code or reimplement everything yourself.
C++/C have a similar legacy burden and barebones platform (although C++ std lib now-days is much better, historically it was unusable which lead to a lot of code duplication) but because the barrier to entry is much higher I'd say an average C++ dev is much more competent than average JS/PHP dev and the libraries commonly used in C++ world are obviously higher quality than JS/PHP (tooling fragmentation is a different story because of the unique problems C/C++ face, but still C++ build tools are probably the most sophisticated out there - out of necessity).
Rust falls in to the category of languages designed by competent people without legacy burden and tackles a complex domain (again higher barrier to entry), most languages I've seen in this category have decent tooling and design (eg. C#, F#, Go, Dart)
Regardless of rust's design in particular, static safety and lifetime management is now non-negotiable, and rust's error messages definitely lead the way although everyone else's are pretty good now.
If D and Rust had a baby I wouldn't anything else.
Gradle does not do any user research. It never sits down with independent developers, newbies, or seasoned pros. It never tests its error messages. It doesn’t do developer relations. It doesn’t spot bad implementations in the wild and offer helpful suggestion. It doesn’t relentlessly focus on making things easier for users.
Many times I wanted to just ask some library/framework authors that very question, like ‘hey, do you know what real apps using these paradigms look like? Do you think it’s okay for even the most basic apps to look this mutant?’.
The ergonomics of developer tools/libraries is an underrepresented topic. Some would even call it taboo since so many of us don’t want to appear as if we don’t know how to use a technology. It stops us from giving valid feedback in many cases where something is truly unintuitive, or painful to use.
When Gradle got popular the main competition was Maven which involves copy and pasting random pieces of XML from the internet, gave stack traces rather than clear error messages and didn't used to support incremental builds.
XML & Maven has the benefit of having a schema. This means that not only do all my pom.xml files look similar, but the IDE can do meaningful autocompletion and semantic checking for me. It’s verbose and ugly, but at least it’s consistent and has good tooling.
Gradle on the other hand is a Turing complete system, which just adds another layer of potential errors on top of Java compilation, which is the exact opposite of what I wanted. Now also all of my Gradle files are different and I have no idea why, and what the relative pros and cons of each style is.
Could Maven be improved? Sure. I’d kill for something like Cargo for Java. But I did not want to find myself debugging by build tool’s state because I used a setter wrong (true story).
Also, I don't see that this is a big deal when dealing with a declarative paradigm. In fact, cutting and pasting random bits of declarative code makes a lot of sense to me. You don't need to understand how it's doing what it's doing, only that it will.
I have yet to discover a situation where some custom thing I needed couldn't be handled with the Exec Plugin or a custom plugin, which are easy to write. The truth is that these uber-custom things are actually pretty rare if you stick to conventions.
Simple builds have simple config. Doing more complicated things means you need to learn how the tool works, and that can certainly take some time.
I'm really liking the new kotlin DSL, having great IDE support, really makes writing gradle scripts much more pleasant.
I do a fair bit of work on node based projects, and the building tooling in the node ecosystem is just so horrible in comparison.
Builds need Turing completeness surprisingly often. Build languages then expand to accommodate, with spaghetti disasters as the result.
“But it has to be accessible to new users and can’t be tied to just one language” said SBT as it jumped the shark with its .sbt files.
Sigh.
Developers get crap tools because developers won’t pay for tooling - if it’s not open source and free we don’t touch it outside of an enterprise environment. And Lo and behold, most tooling is either cobbled together by Enthusiasts, or as user friendly as enterprise ware usually is.
(There are, of course, shining exceptions to this, which is great)
Even when I'm writing Scala I prefer Maven to SBT, I just prefer declarative build tools to DSLs up in my grill.
And sbt is just bad all around on top of all this, and it should feel bad.
To be fair, this is due also to the fact that opensource tools are mostly guaranteed to outlive their developers. Should Microsoft go bankrupt tomorrow (don't laugh, it basically happened to SUN), most of their traditional tooling would disappear. In contrast, Gcc might be old and crusty, but it will go on forever like a cockroach.
I'm not sure i understand this argument
crap Linux because developers won’t pay for Linux
crap git because developers won’t pay for git
...that's clearly not the case, no? Every day we use a bunch of developer tools that are not just crap, but amazing and great to work with, and were built as open source. So I'm not sure about equating open source with crap experience.
As a side note, this is probably (intentionally or not) the same tactic that Microsoft used in the 00s to avoid competition: keep changing things fast so that anybody using your tool chain a) has to follow your lead to keep up, b) ends up with huge sunk cost, and c) (if they trust whoever's behind Gradle) keeps being reminded how much they innovate.
Not to mention that build config is probably the absolute last place you want a weird unreadable language nobody knows, because it tends to be touched so rarely that nobody builds up any familiarity with it.
Here's the javadoc: https://docs.gradle.org/current/javadoc/org/gradle/api/Proje...
If you switch to the Kotlin flavor of Gradle then this goes away. You just have to make the compiler happy then, same as for application code.
This really rings true to me. I almost cringe when I have to explain how to do some of the most basic things to non android developers.
Aside from the dozens of libraries you need to include, and need to know where to find and what version is compatible with what, the tooling itself is an insurmountable mess for new developers. It's also not getting any simpler.
Like I was interviewing a guy for an Android role, and he was walking me through a pet project of his which consisted of literally one list view which updated it's contents to filter results out of an SQLite database as you type into a text field, and the project had literally dozens of files. It just made me sad.
I mean maybe there is something I don't understand, but the state of the field seems to be heavily driven by following what the "cool kids" are doing, and the tooling seems to have more to do with signaling that you're in the in-crowd than it has to do with actually delivering value.
That's most likely overengineering. The only things you need for this are:
- An Activity
- Eventually, a Fragment, but you can do your work in the activity directly
- A database, optionally in another file to abstract out direct queries, but it can simply be direct SupportSQLite queries. Just, not on the main thread.
- A viewmodel if you don't want to hate yourself and write logic in your Activity, making it basically untestable.
- A layout file for your activity/fragment.
- A layout file for your recyclerview list element
That's 5 files. 6 if you include build.gradle, 7 with AndroidManifest.xml
This can all be written in under 500 total lines.
Library wise, all you need for that is androidx and recyclerview.
After that, you can very well put your activity/fragment/database logic in the same file, if you know you're never going to maintain it.
Compose is still in dev builds. Expect to see alpha in a few months, betas in two years and RCs/Release in three. Not exactly "soon".
In racket/gui this could be maybe 100 clean lines of code in one file.
Android is complex, yes. Sometimes for no reason (hello God Activities). But sometimes, it has good reasons to do so.
I actually wrote one, with the bare minimum. HNActivity.kt is 81 lines and handles updating the list, querying
import android.database.sqlite.SQLiteDatabase
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.widget.addTextChangedListener
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kotlin.concurrent.thread
class ListItemVH(val view: TextView) : RecyclerView.ViewHolder(view)
class HNActivity : AppCompatActivity() {
private lateinit var recycler: RecyclerView
private val adapter = object: RecyclerView.Adapter<ListItemVH>() {
private var _items = listOf<String>()
fun setItems(newItems: List<String>) {
DiffUtil.calculateDiff(object: DiffUtil.Callback() {
override fun getOldListSize(): Int = _items.size
override fun getNewListSize(): Int = newItems.size
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) =
_items[oldItemPosition] == newItems[newItemPosition]
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) =
_items[oldItemPosition] == newItems[newItemPosition]
}, true)
_items = newItems
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ListItemVH {
val v = TextView(parent.context)
return ListItemVH(v)
}
override fun getItemCount(): Int = _items.size
override fun onBindViewHolder(holder: ListItemVH, position: Int) {
holder.view.text = _items[position]
}
}
private var search: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_scrolling)
recycler = findViewById<RecyclerView>(R.id.fab)
recycler.adapter = adapter
recycler.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
findViewById<EditText>(R.id.toolbar).addTextChangedListener {
search = it?.toString()?.takeIf { str -> str != null }
}
}
private fun performSearch() {
thread {
val db = SQLiteDatabase.openOrCreateDatabase("items.sqlite", null)
val query = when (search) {
null -> "SELECT text FROM table"
else -> "SELECT text FROM table WHERE text = ?%"
}
val args = listOfNotNull(search).toTypedArray()
val res = db.rawQuery(query, args)
val text = mutableListOf<String>()
while (!res.isAfterLast) {
text += res.getString(0)
}
adapter.setItems(text)
res.close()
db.close()
}
}
override fun onResume() {
super.onResume()
performSearch()
}
}
Add in another 30 lines for the Activity layout, and you've got your thing.
You've also got every single developer you'll work with hating you, and you'll hate maintaining this code. Just in those few lines, so much can go wrong (thread gets started and activity gets backgrounded? Oops, you crashed.)However I think it should be possible to design interfaces that make simple things simple while still giving you the flexibility to do complicated things. I think neither Android nor racket/gui has accomplished this, but at least racket/gui is fun rather than tedious. I agree with the article that there's a lack of user-centered design in the Android scene.
Or you can pull in a library that seems to be popular and maintained and solves the problem that you have.
At least it's better than some other ecosystems, where it's "this thing is deprecated now. Nothing replaces it and you can't do similar functionality any other way, it's just deprecated." Or maybe I just haven't gotten to that part yet.
I precached but it still required internet access to get some maven deps for android build. Updating is full of hassles and gradle, as always, unexpectedly fails somewhere eg in "assembleDebug" step. Maybe some version mismatch and I am not in a situation to do clean installs that require GiBs of internet. And no guarantee it works even on clean install.
I have come to a conclusion that front end (web or app development) is a mess and it is not for me. Look at Go, or Rust to know how to keep tooling simple and straightforward.
All others provide you a native simulator that you can use to ease development ( Windows even provided one for Windows Phone). Android provides an emulator that gobbles up RAM and is painfully slow to use. Most people just prefer to use their local device instead.
Other platforms provide you their own IDE tailor-made for development. Google just took IntelliJ and made some changes it.
Edit: Another point is compile times. Compile times for Android are just atrocious. While Windows and iOS compile quickly in a few seconds and allow you to fast deploy, Android is painfully slow taking over a couple of minutes for compilation.
Tensorflow is another data point to say that you are wrong.
That's not my experience. The official Android emulator works just like any emulator and will take the RAM it needs to run like a real device. If you don't have the RAM to emulate a smart phone (which these days contains anywhere between 6 and 10GB of RAM it seems) while also running the debug tools, you can always lower the RAM you give the virtual device. Android will run on 1GB of RAM though it won't be pretty if you want to emulate interacting between different apps and such.
Oh, and there's a potential gotcha in that Android does a lot of stuff on the mobile GPU, so GPU acceleration will make a huge difference. You don't need a 2080Ti to run the emulator, but integrated Intel graphics are often not up for the task. On my laptop the Intel drivers were also flagged for incompatibilities because of unresolved issues with the driver themselves, making Android run on software rendering and oh boy what a drama that was.
The big problem isn't so much the emulator, it's the IntelliJ-based IDE combined with the java compilers that gobble up RAM worse than Chrome. Each compilation process can quickly grow to 2GB and release it soon after, torturing your OS's memory manager.
My experience running and debugging the emulator with 16GB of RAM have been quite good. 8GB is doable if you don't have too many tabs open and tune your emulator.
The biggest problem I have with Android development is that compiling even a small app takes over a minute in my 7700k.
As a fan of IntelliJ, I applaud their choice of IDE. They used to base their IDE on Eclipse and that was clearly not worth it. As for dedicated IDEs, unlike Microsoft and Apple, Google didn't have an existing IDE lying around for their purposes. When someone did make an IDE from scratch, Xamarin, I found it worse in every single aspect aside from language support. And let's be honest, while Visual Studio is probably one of the best end-user facing pieces of software Microsoft still maintains in-house, it wasn't that great. Typing latency is high, configuration is painful and error messages are vague. Many, if not most, dotnet devs run the Jetbrains tools to enhance their VS experience.
And I wholeheartedly agree with your point about compile times and have added it to my comment.
iOS doesn't actually emulate most of the system though, which is why it can run on so little RAM. Apple repurposes the shared components running on macOS to do the heavy lifting of iOS emulation. Their approach, in my opinion, is closer to a Docker container with a display than to a traditional emulator.
This is also why it's technically impossible to port the iOS emulator to Windows or Linux even if they were willing to give up their vendor lock-in.
I haven't worked with the iOS simulator for long, but I found some features the Android emulator offered missing in iOS. There was no installing apps (like WhatsApp, to test sharing) and the tooling for faking following a recorded or generated GPS path were either impossible for me to find or just not there. I also found that the "OS" inside the simulator felt bare-bones and incomplete whereas the Android emulator comes with the complete toolkit, including Google Play. iOS simulation is faster than Android emulation, but in my opinion that comes at a cost.
It's possible to do the same thing on Linux through Anbox, though that might not have all the same API's. The Android kernel is quite different from the Linux kernel it was once just a fork of and on Windows you'll never see the same level of integration with Android that iOS has.
Android Studio isn't necessarily that bad. On a very large project, my AS instance eats up, at the absolute worst, 4GB of RAM. Which, compared to all other IDEs is not that awful.
Gradle however will eat memory and balloon to 8GB on its daemon
>The biggest problem I have with Android development is that compiling even a small app takes over a minute in my 7700k.
Make sure incremental compilation is setup. For small changes, this means that recompiling is lower than 10 seconds.
In fact, you don't even need VS as part of the experience anymore – Jetbrains Rider has most of the Resharper features (including "extract class" in the latest version) in an IDE based on the IntelliJ platform. It feels much faster than VS + Resharper.
Why can't it be as simple as a bunch of files in a folder, a build file, a manifest, and an asset directory? That's what most apps need.
These are Google engineers we are talking about. The same people who bring you gcp. Why are you surprised.
I don't think it qualifies as speculation anymore to assert that Google has something really broken about it, organizationally, that keeps it from producing truly good software—and often not even decent software—in most of its projects, for some reason. Even very high-profile ones. There's not really any other explanation for how they consistently behave.
The rest of Google has other priorities though. Even gmail’s iOS app had pretty glaring bug that it couldn’t mark a message read.
Google docs fails to save at times. YouTube Kids is just an awful mess. Don’t let your kids even near it. Google Cloud isn’t as reliable as the other clouds. Google can’t do proper human support. They don’t believe in it. If you’re a small shop, going with Google for anything critical is just going to be a disappointment.
Google Search though. I have an insane amount of respect for their engineers who work on performance and reliability.
I think what these arguments miss is the upside of today. The "good old days" of brilliant developer experience was only brilliant for a small number of people. You had to learn right way of thinking, invest the right amount of time to learn good principles, how Linux was architectured, how not to re-invent the wheel, and so on.
This was a huge barrier to many people getting started with coding and the economics demanded getting more new developers ramped up faster. This pressure caused people to try to quickly develop little "get started quick" ecosystems.
Now we are in a situation where from a pure software engineering perspective we do have a complicated stack of teetering and shifting frameworks, I agree. But on the other hand this has come with enabling an awful lot more people to quickly get started toying with apps and coding.
What I'm trying to say is, these days its quicker to go from Level Zero to Level One, even if that means you've shot yourself in the foot going from Level One to Level Two.
I see this a lot as well. And frankly it scares me. It should not be very easy or quicker to go from Zero to One at the expense of footgunning One to Two and beyond.
Practices and culture are easily learned when you are just starting. And those practices stay with you till the end. It is very difficult to unlearn something, especially when you learned it while starting out as it becomes a kind of second nature.
Those practices might not harm when you are making basic sites or demo apps in your spare time. But when an engineer with these practices goes to work on something like the software for controlling Teslas, it suddenly becomes a big problem.
In my view, it is better to take some time to make a solid foundation than to quickly make a leaning tower of software that could, in these days of software in everything, lead to catastrophe.
Well, the reality is that most businesses only need basic sites and you don't need trained engineers to build them. Basic developers can do it as well and giving them the tooling to do so helps.
There is a lot of value in making programming for "basic" stuff like a CRUD web/mobile app about learning to use tools rather than learning to understand the whole system (i.e. programmer vs engineer).
A comparison I really like: To build a house, you need one engineer for statics, but a lot of construction workers actually putting it together. And even that one engineer for the statics is more for safety than actually necessary, if you build some basic house with the basic default material. Because it's proven over and over again that it holds and the margin for errors is quite large before there is any risk of collapse.
Uncompress tgz
./configure.sh
Missing dependencies
Find those tgzs
./configure.sh
Missing dependencies
Find those tgzs
One won't run on your system
Try to hack the dependency out
Give up, you didn't really need that anyways
Another thing I wish it would do is give you all of the missing dependencies up front. Aain, not so bad now, but it was a major hassle for me when a missing dependency meant a long walk to the local library, 4gb flash drive in hand.
The seedy underbelly of all of this stuff is that most of the libraries and dependencies and pet projects that somehow became ubiquitous never worked outside of the lone developer's laptop, or the one compsci department's ancient from-scratch installation, etc.
And if you asked him why it didn't work he'd tell you to go fuck yourself.
Apt is still here.
The author is not pining for the good old days. In fact, he specifically says that when he was starting out, the tooling was terrible.
But I found the examples given in this blogpost striking: https://tonsky.me/blog/good-times-weak-men/ One of the most prominent tech companies (Apple) released a new music program to replace their older music program. The replacement program included several easy-to-notice UX bugs.
The bugs surely aren't from a lack of attention, expertise, or resources. And while obviously it's not easy to write a program like that, I think the best explanation would sound something like "towering pile of shit".
10+ years ago, you could just install a server-side rendered web framework on a VPS or dedicated server you rented from a hosting site, and you'd experience a far smoother developing experience compared to today's Javascript churn. I'll take Django over React+Node.js+Docker+etc. any day.
And compare modern devops to ye olde webmaster ways. It's much easier to get started by writing a few bash scripts and customizing Nginx configs than to learn how to use Kubernetes, Docker, Terraform, etc.
Nowadays you can get cheap internet access, and you can buy a used okay-level laptop, get a free domain (like .tk, just a bit better from Google, the .dev), get a free VPS from AWS, and you can run whatever you want on it.
PHP has a built in web server, you can start developing with Laravel/Symfony easier than ever.
You find DigitalOcean tutorials for basic things easily, and you don't have to try to make stuff work on shady adware shared hosting portals.
The old webmaster days. Uh those were horrible. People tried to do blue-green deployments with Capistrano and PHP cache invalidation with atomic rename. And then folks spent years hacking RoR stuff to make it scale.
Yeah, Django is nice, but Python is a mess. Now with mypy finally there's some light at the end of the tunnel.
The problem with the above is that the easier it becomes to go from Zero to One, the more _replaceable_ One becomes. After all, you can learn it in no time!
Naturally this leads to more and more churn and every developer who is already _at_ One or above has to keep going back and re-learning it in order to “keep up with current trends”.
After a while it becomes tiresome to see yet another front-end state management library promising to fix all the problems caused by the previous iteration... Slowly you become jaded enough to find yourself subconsciously hoping these projects fail so you don’t have to waste your time learning their specific _dogma_ (because of course it can’t just be “entities”, “events”, and “handlers”. This new _snappier_ API uses “atoms”, “signals”, and “transducers”)
The reality is that most of the _real_ problems within a system (at least the interesting ones) are are at Two and above. So the constant churn at One just becomes an annoyance that takes focus away from the important bits.
I’d _gladly_ take some churn at the level of solving _my_ domain problems. Unfortunately, thus far, it seems I’m the only one writing anything that fits that bill...
Nothing has changed, today's web frameworks are pretty much worse on all fronts than the mess of excel, access, vb apps written by people without any formal training. At least many of those utilities worked for years after being created, unlike this mess which requires constant reworking just to continue working.
This is the main appeal of Rust to me.
I get error messages that contain the word "perhaps" with a gentle suggestion on what I might have wanted here instead. Quite often it's correct.
> The compiler would say “Missing semicolon on line 427.” To which I’d reply “If you know what is missing, and you know where it is missing, why not add it back yourself? You useless piece of crap!”
How can you add a feature like this without any extra complexity?
It sounds great in theory but in practice most code depend on another piece of code. Most of the time blindly changing one line can introduce more bugs.
Using indentation for control flow was a bad decision, even if most modern IDEs now have good solutions for it.
Doing code formatting is fine; doing code formatting on lines from a checkout that I didn't change is bad. Yes, the file should be reformatted. But let me know before doing it, so I can make a formatting-only commit so it's easier to review when I put up the PR.
"Expected a colon here, found a <" (ascii-art pointer to the place) "did you forget to close a bracket here?" (ascii-art pointer to 12 lines above that, where, indeed you forgot to close a bracket.
It does this for semi-colons (they are not always required), closing brackets (maybe you are nesting something horrendously deep?) etceteras.
The idea, basically is "I see this is wrong. But I'm not the one to decide how to fix it for you, but here's a suggestion".
I love Rust for this.
1. Files are allowed to have different numbers of open & closed brackets. So who knows where the real error is? For all the compiler knows, you left that paren open deliberately in one file and forgot to include the paren-closing file?
2. C++ syntax generally lets you put anything anywhere -- the compiler doesn't seem to mind that I'm defining a new class literally inside the parentheses of a function call? Is that actually legal? Who knows. But I'm still a little confused... after opening that paren I almost immediately use too many closing braces -- why isn't that spotted immediately?
It was a complete nightmare trying to port code that compiled and ran perfectly well on the dept microvax when we all transitioned to Ultrix and Linux.
As a result of those months of pain, even thirty years later I’m a huge fan of strongly typed languages, compiling with -Wall and -Werror, and have a zero-warning policy.
If you’ve never gone through it, you wouldn’t understand...
It's great to not have stuff just rot behind your back.
(True for the language, not quite true for the ecosystem, since the switch to modules fucked a lot fo shit up, with some tooling _still_ not back to pre-modules levels of quality.)
When you're designing an error message, don't just tell the user that something went wrong. The mere fact that they're getting an error message already conveys that. Instead, concentrate on telling them what is being expected or what they can do to fix it.
1. Identical toolchain. 2. Identical dependencies.
These two principles still apply, at least for the toolchains that produce binary/bytecode/bitcode artifacts, and we have much better tools to achieve these goals.
Since VMWare became popular, virtual machines have made the goal of maintaining identical toolchains trivial: we simply maintain snapshots of the machine image used for the builds.
The Web, or more specifically the HTTP transport, has made dependency management infinitely better. The Web has changed the world but it seems that the software development community has not imbibed the RESTful principles described by Roy Fielding. URLs, especially for software dependencies, should be forever. Support for URL forwarding should be a requirement for package repositories.
I am also flummoxed that the Cache-Control and Concurrency-Control built into HTTP is not leveraged. We have conditional requests that use ETag or Last-Modified headers and the Content-MD5 header has been part of HTTP for a long time. As far as I am aware, Software Configuration Management tools tend to do the same thing out-of-band. ETags are opaque, but if I was designing a new package repository, my ETag would be the SemVer, optionally annotated with the target architecture for binaries.
I thought the entire point of automatically generating boilerplate was that you could ignore it until you needed something other than the defaults. When all of that magically introduced complexity is wrong, why make me start with it in the first place?
If you were able to install everything, including the documentation, to your hard drive you never really needed anything else (which was good, because I didn't have internet access at home to begin with - too pricey).
You had high quality development and debugging tools, and then in MSDN you had this absolutely exhaustive, encyclopaedic documentation that covered every possible topic in depth, and a good enough search function that you could usually find what you were looking for without too much difficulty.
In some ways - not all ways - those were the days.
And when they do follow semver, the pattern becomes obvious. We're on Guava 29 after all https://mvnrepository.com/artifact/com.google.guava/guava
19 breaking changes in ... 10 years?
I really wonder why are we letting this happen. Really
A similar thing to when I see a command or an API call that does what you want then says "deprecated" (with no clue of why or how to do that now). Sure, because nobody does this anymore right? (and I'm not talking about complicated stuff) Or the new function works in some mysterious way when the old one was straightforward
I mean, really
But it seems people are "moving fast, breaking things and who cares if it's broken? it works for me"
Hm, that is actually a point where (in my experience at least) the Java community tends to do pretty well: in nearly all cases where I see an API that is deprecated there is a comment that at least tells you what you should use instead. Additionally, "deprecated" usually means that it still works, which is a whole lot better than having it simply disappear.
In my experience, many of the Android methods marked as deprecated also contained docstrings to the new API and how to use it. In some cases it can even automatically insert the boilerplate code to do what the old, deprecated function did with the extra error handling for changes to the platform.
compile and implementation aren't the same thing. Depending on your use case, compile needs to be either changed to api or implementation. They aren't the same thing, so just a quick replace by the IDE would likely break more than it fixes. The problem is that most guides treat them as the same thing and make people think the compiler is being stupid for changing the word they need.
I can't speak for other projects, but for Android, where most of my deprecation struggles came from, the change was the result of changing requirements. Early Android was a wild west where every app could copy the contents of your SD card and upload it to their servers without even so much as a warning, where IMEI numbers were widely used as identifiers in databases for analytics and where permissions were more of theory than a practical requirement. Killing random apps was a permission any application could obtain, app databases appeared on your SD card which sometimes included credentials and secretly turning on cameras was easy to do by rendering the preview to a 1x1 pixel area. Screen hijacks were also commonly possible, causing major concern for apps for banking and such. Google had to retroactively restrict applications with new permission systems and applications often crashed without workarounds because the developers often never bothered with exception handling for stuff like file I/O. This isn't moving fast and breaking things, this is trying to fix fatal flaws in the design of a running train.
When I encounter deprecation, it's because the underlying design of the API I use has significantly changed, often for good reason. It's annoying when features turn from a simple function call to a complicated code structure, but there's often a good reason behind it. I just wish people would put those reasons in their docstrings and release notes instead of marking stuff as deprecated and leaving it at that.
It’s about being conscious about others time and the impact your work has on theirs.
They might be a good reason for changing “compile” to “implementation”, but it costs nearly nothing for the dev to add a specific rule that if “compile” is in the code, the compiler should show a link or point to the docs or explain how to solve it.
On the other side it’s painfully costly for anyone using that tooling to understand what’s happening, why the change, how to solve it. The end result is that a lot of inexperienced users will find a fix that works for them (like bulk replace of one for the other), and the end result tends to be a worse ecosystem for all.
> Why doesn’t Android Studio / Gradle / Whatever just go “Mate – you’ve used compile here – that won’t work. Want me to automatically update the syntax? It’ll probably be fine.”
But the machine can't make the decision, since it doesn't know the intent. The informed human in charge of the project does. What if there's automation, and tool ends up making the wrong decision? I bet that the author would be complaining about that instead.
Reading the Twitter threads that the author links to are quite illuminating, and it's surprising to me he feels it's showing him in a good light. It's an amazing display of both entitlement and intentional ignorance.
I also touch python once in a blue moon. It's always frustrating. To get anything done, I need to install python, then install pip, then make a virtual env, then use pip to install the libraries the project needs in the virtual env, then start the project. At this point, I realize the project does not work on the latest version of python, but on some specific version, and I need to throw away everything and start again. And each time I tell myself: "why wouldn't they write this obviously critical piece of information on the main page?"
I don't think one of the tech stacks is much better than the other. I think we just perceive the one we are knowldgeable in as better, for obvious reasons.
Oh, that's where the simple projects "start the project".
Even with wheels around, that's where you'll get an error message that eventually decodes as "There is no C-Compiler on this platform". And then you're installing clang or gcc. And make. And configure. And fuckit, just install build-essentials.
And then you start hunting C dependencies. Which I can because I have some experience building C, enough to never to it again. And then you start learning which link error with libpython indicates which minor version of python is actually required. And eventually you start looking into really weird hacks with chroots to be able to have multiple python-devel headers around. Or use containers to compile a python library.
Sorry. I like the language python. But installing python packages to do something... I don't know, it eventually hurt something inside me. I severely never want to deal with python in production anymore unless I can just globally install it in a container and stop caring.
Java tends to be tolerable. People tend to overuse maven beyond all repair, and gradle can be funky. But none of them compare to the hell python requires.
These days i spell that
poetry new myproject && cd myproject
poetry add dep1,dep2,...
poetry install
git init .
gi python && git add .gitignore && git commit -am “first commit” curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python
pip can be used by itself to install a poetry managed project, so just venv/bin/pip install
as normal.This is ok-not-great; i’d prefer for python and pypi to put some official funding and weight behind one de-facto 517/518 build system and stabilize things a little. However this is python we’re talking about, so it’ll never happen.
With poetry init, the tool walks you through project setup
I could build the code in IntelliJ IDEA, but I couldn't for the life of me figure out how to actually run it to simply test it and play around with it. I can only concur there is something seriously wrong with the Java world/community. Perhaps it's too focused towards enterprise, where you can afford to have one person specialise in sitting at a desk, monitoring Wildfly?
Building a jvm project these days can be a matter of cloning the git repo and running `./gradlew build`.
Because what you mean and what the closest syntactically correct program to what you typed are two very different things.
The alternative is to deal with computers how they naturally "want" to be talked to. Then life is simple, you just need _engineers_ to write the software, and to _train_ people to use it.
A good (read: native) and well-architectured desktop app probably has more in common with backend development, UI design aside!
I mean that it always seems to relate to things with graphical interfaces. To be clear, I don't doubt it's possible to create well-architected things with GUIs.
It's not unfair to group client dev together. Client dev is always hard. People on HN who only have touched JS dev tend to think that JS clients are uniquely hard and, presumably, that clients are a cakewalk to build on other platforms/sdks.
Never was true. Just look at the Android dev comments ITT. It's all hard and complex.
How many hours and thousands of lines of pointless tests have been wasted because of bad type systems - I really don't get the fuss about about Python, C++ was designed by a committee but python genuinely feels like it wasn't designed at all beyond the basic syntax (which I like)
But like most interpreted languages, the up-front friction is low, but it starts to buckle under it's own weight when a project reaches medium size.
A counterexample is the company when I worked for 8 years. They took Python very seriously. By the time I left, 4,000 developers were making 20,000 commits a week in a codebase of some 30,000,000 LOC.
import library
library.function
That doesn't prevent writing a different value into library of course.Maybe people should choose their tools based on mature technology stacks rather than whatever happens to be cool this month.
More and more, though, there’s such magic being performed by complete and libraries that you sometimes want DAIS.
As with the missing semicolon; the compiler can't be sure if that's a typo, so the line should end there, or you just really missed the semicolon only. It doesn't understand the code the way you do.
As for "Don’t make me think." - the whole software development is about thinking. Can't do any part on autopilot.
Sure, but I'd prefer to think about the problem that I'm solving, and not about which combination of packages I need to successfully compile and run the application.
I don't want to spend time manually upgrading dependencies, or syntax, especially if this is something that can be automated by build tools or IDEs.
One thing that doesn't:
> To which I’d reply “If you know what is missing, and you know where it is missing, why not add it back yourself? You useless piece of crap!”
I had a friend at school who used to make the same complaint about BBC BASIC back in the 80s. If you missed a double quote you'd get an error something like:
NNN Missing "
where NNN was the line number.Thing is, and this is true in plenty of other cases, if the computer guessed at where to insert the " and got it wrong, especially if it didn't tell you what it had done, this could lead to some pretty weird side-effects in terms of program behaviour that might be harder to debug/fix. Same is true with other languages. I mean, even if it did tell you, how many people actually read compiler messages other than errors every time they compile?
Nowadays, with background error highlighting it's pretty much a non-issue.
This, on the other hand...
> And don’t make me waste my time fixing problems that you have caused.
F###ing f### me but this boils my p###. I am sick and tired of, particularly JavaScript, architectural astronauts making breaking changes - often changing fundamental concepts - in their APIs and libraries with little consideration to the effect it will have on their users. It's particularly irksome when they've actively evangelized to build that community. Angular, D3, gulp - even TypeScript - have all made significant changes that have wasted massive amounts of my time, and the time of plenty of other developers I know.
It's not like I've gone out of my way to use OSS without "contributing back": often these are inherited projects. For my own side-projects, I try to avoid using libraries where I can because I simply don't want the hassle. (Although it's basically unavoidable, particularly if you use Node as a back-end.)
Still, you can't escape it entirely even if you limit yourself to browser APIs. There have been changes in behaviour around Web Audio, accelerometer and compass, and full screen support that have broken my code out of the blue.
It's really tedious.
Back in the day I remember that Java was much better for this: you could be fairly confident about running Java 1.0 and 1.1 code unmodified on Java 1.5, at least. It's been years since I've been in the Java game so it sounds like that might have gone to the wall now as well, at least when it comes to dependencies outside of the JDK itself.
As a lot of things, this is a matter of how often it gets it wrong.
For instance in ruby world, rubocop has an option to autofix. To your point, it makes it clear what it fixes so you can review. It’s also conservative enough to bail on anything that could have strong effects, like block endings.
That said in years using it I can’t remember any time I really regretted making it autofix stuff.
Most recently it was trying to get a laravel PHP app to work on my 2015 MBP, (which apparently was using a system installed version of php, preventing me from connecting to a postgres database on AWS using SSL because it wasn't compiled with SSL support...)
Today it was an ios app that started to fail compile because 1 of the devs used an ios 13.0 only feature.
Last week it was a .net core windows service, that I had to read several pages of documentation on, in order to upgrade from 2.2 to 3.1, in order to simply add telemetry reporting.
I feel the premise of this post is more around the concept than it is specifically about gradle issues. It's simply this feeling of drowning in just getting something back to a working state through the endless cycle of trial/error, StackOverflow, and googling different terms.
Too many times what started as a journey of inspiration when I wanted to add a cool feature/idea turns into hours of frustration. I normally end up getting it to work, but all my motivation is gone and I sit there and ponder how is it possible to be doing this for 10+ years, and find myself constantly in these ruts across the board.
It's articles like this rant that make me feel that I'm not alone!
But even in our world we still have some level of package hell, typically in the form of version redirects.
Anywho, back to the case of Android, it seems as though nobody ever thought about what it would be like to rapidly iterate the way google does on a toolchain that tends towards liking things to stay in place.
It feels better to someone to change 'compile' to 'implement' and so it happens. It ruins part of the contract between the software and existing users, but the users get it for free and they're not organised so who cares?
I agree with virtually all of what you say apart from automatically updating dependencies that may or may not work. You're having to work around a problem that shouldn't exist and I think an IDE making dependency choices is a bad idea. What we really need is ONE build tool and package manager and a standard way of using them for each language.
We seem to have this idea that a large choice of software tools means better software tools. Just not true. We are not completely rational 'consumers' in this economy. New software adoption is hard and has a cost associated. There are large insentive to stay with your current tool chain. Software companies knowingly or unknowingly exploit this by not having to really consider their users.
Microsoft, can, within reason do ANYTHING to visual studio as there is no viable option for people or organisations change over to.
I reckon a big reason people love Rust is because no one has fucked it up with lots of shitty 'tools' yet.
I would really love it if a project I made two years ago would still compile, but it doesn't.
I've lost count of the number of times I heard/read, "Just type `npm install flargnie` and it'll all work."
Only it doesn't.
And being unfamiliar with the craptastic and hostile ecosystem that is node's, that's usually the point I just bail and find another tool.
https://git-scm.com/book/en/v2/Customizing-Git-Git-Configura...
It’s a real embarrassment to our industry.
Makes me want XML and Maven back; at least my IDE can guide me based on the schema files it parsed.
Actually built for Linux on my MBP.
Boom, all dependencies, and in seconds a perfect binary. I've had similar experiences with a number of projects, including huge projects like Firefox.
Like it all things in life, it varies. Some projects and toolsets are perilous mountains of crapitude. Others are just magically delightful.
If anything, Javascript clients have the advantage of being able to bring your own fundamental abstractions since you're not stuck having to compile your abstraction to a narrow API like UIKit.
Web development gets the worst reputation because it's the only one most people have experience with.
This is generally how dependencies work across all languages and frameworks, and I think the status quo is extremely inefficient. When a library maintainer releases updates with breaking changes, we waste thousands of developer-hours on upgrades and fixing all these changes.
I've been thinking about a new kind of package manager and repository that strictly enforces a certain level of test coverage, and also forces library maintainers to codify the upgrade process. For example, if you decide that you want to rename a `get` method in `v0.1.0` to `fetch` in `v1.0.0`, then your `v1.0.0` release MUST include an automated code-mod script that automatically runs when a developer updates the library version. This wouldn't work so well for dynamically typed languages, but this rename example should work flawlessly in a statically-typed language.
The code-mod script should also include lots of test cases, and be tested on lots of other real-world libraries. Similar to how Rust developers detect regressions in the Rust compiler by building a large number of crates.
There's probably a lot of reasons why this wouldn't work in the real world, but one can dream! There was a lot of controversies when Elm made this kind of decision about their package ecosystem and removed native modules [2]. Deno is working on a high quality Standard Library [4] with no external dependencies, and where all code is reviewed by the Deno core team (and presumably with a high level of test coverage.)
[1] https://github.com/rust-lang/crater
[2] https://dev.to/kspeakman/elm-019-broke-us--khn
That’s not the role of a compiler, for good reason. I stopped reading the article after reading this line.