Python's mess of dependency and venv management system is probably equally bad, although my experience was that Poetry is decent.
By contrast, Ruby tooling usually works well (you can choose between chruby, rbenv and rvm, but they all work similarly, and Bundler just works(TM)), and Java has its warts but one of the best IDE experiences I've ever seen. Maybe the refactorings aren't as safe as in Haskell, but they're extremely easy to do.
I don’t know about today, but ten years ago the answer was to hop on IRC and you’d get all of your IRC-sized non-FAQs answered, however difficult they are.
Recently, i upgraded Ruby, but then what's to do about Bundler then ? I'm confused.
There was a long and drawn-out transition, but these days it’s quite simple: use GHCup to manage your tooling, and Cabal to manage your packages. At one point Stack was the best option, but no longer: it’s not as well maintained, and is missing a lot of features (see e.g. https://discourse.haskell.org/t/6849/23).
Yes, there is a disagreement about whether to use maven or gradle, but IMHO they both work reasonably well out of the box.
I'm not a huge fan of the gradle toolchain tool (I don't think gradle should be managing my JDKs and I would prefer if it could instead just fail the build if you use the wrong JDK version), but I do understand why it exists.
Right now the Mac (M1 or M2) users in our team can't run our integration tests. It has been suggested that if we migrate our 50-100 services to Java 17 then the tests will work for them again. Trying to do this breaks the Gradle scripts, and the errors are so vague I can't tell why it's complaining.
(And I'm not even that new to Haskell. It's just that I don't use it every day and when I come back to it and have to remember the weird incantations and dances I have to perform to make HLS not crash or want to overwrite my stack-installed Haskell version, I'm usually rather annoyed.)
I merely stated that I have no trouble running old projects using stack.
> I don't really think that you've given an argument that is persuasive enough to someone who is new to Haskell.
Honestly, cabal improved a lot since the old ages. For a beginner, either stack or cabal should be very fine imo.
> or want to overwrite my stack-installed Haskell version, I'm usually rather annoyed.
Not sure why you would want to do that. Either you use stack and let it handle your ghc install, or you don't, and I really don't see why you would use stack only for compiler install.
I was referring to the linked forum thread.
> Not sure why you would want to do that. Either you use stack and let it handle your ghc install, or you don't, and I really don't see why you would use stack only for compiler install.
What I mean is that when I start VS Code on my Haskell project again after a couple months, it tells me that it needs to donwload a new version of GHC. And I don't know why since I didn't change anything about my project. And when I try to click on update, it fails and the HLS extension crashes. I can't tell you where exactly this goes wrong, but it's obvious that these tools don't all work well together.
edit: Now I see that my wording was confusing. You quoted me as saying that I want HLS to overwrite my GHC version, but what I meant was that I want to prevent HLS (actually, I should have said the VS Code Haskell plugin) from doing that. The subject of "want" in that sentence you quoted is "HLS".
Apparently there is some work being done to improve the stack <> hls experience, but I wouldn't know how it's going and when it's being delivered: https://github.com/commercialhaskell/stack/issues/6154
I personally wish that page were even more opinionated, but it's politically tricky.
What's really surprised me lately is how much better OCaml's opam tool has gotten! Last time I was using OCaml in anger, the experience was very, very clunky, but these days it's quite smooth.
First, a nitpick: it's pyproject.toml. YAML was considered for the language of this file and rejected in favor of TOML.
Second, unfortunately, I'm not sure I share your optimism. This is an area where IMO the Python developers have never been able to do things right. In fairness, they are trying to support a lot of very different use cases, but the proper way to do that would have been to let the communities surrounding each of those use cases invent their own tools, while focusing the standard tools in the Python standard library on the vast majority of Python projects that are pure python code--no extension modules, no weird compilation issues, just pure Python modules and packages for which build and install ought to be simple and straightforward. But even that simplest use case was never quite properly and standardly supported.
Even pyproject.toml illustrates this pattern: a new markup language was adopted, one which has no support in Python's standard library, which has no obvious advantages over previous file formats, and which now creates ambiguity between pyproject.toml and the previous supposed "standard" for declarative project metadata, setup.cfg. Which do I use now? I can't just use pyproject.toml because it doesn't include everything that setup.cfg does; and I can't just use setup.cfg if I want my project to be compliant with the latest build tool specs because setup.cfg alone is now considered a "legacy" build format. So now I have to use both, even for pure Python projects where this should have been a solved problem years ago.
At least the actual installation of pure Python projects is now a lot easier; once you have a properly built sdist or wheel, pip install will put it wherever you need. But the "properly built sdist or wheel" part is still IMO a lot harder than it needs to be for most projects.
The fact that the PEP was proposed with this, discussed, accepted, implemented and deployed with this is indicative of the failures of process that lead to many of Python's problems. Python3, async, threads, GUI toolkits, the whole mess of tooling ...
Languages and their ecosystems need someone to say "no". To maintain a pragmatic path forward for users of the language, not developers of the language. Python hasn't got this. With all respect to Guido, and now the core group, they let this slide way too often.
But even so, I must recognise how awful the experience is for new users. It's taken me years to settle into this system, and it can take half a day to get someone up to speed with these tools if they haven't used them.
I also work a lot with non-developers who need to use or contribute to Python models, so that doesn't help — but I bet it would take an order of magnitude less time to get them up to speed with something like cargo. Coaching them has helped me see how user-hostile the process is to beginners.
It also doesn't help how infectious "all-in-one" Python distributions like Anaconda can be, to the point that whenever anyone has an unexpected issue one of my first reflexes is to check their PATH. The fact that Rust has a widespread default toolchain multiplexer completely solves this issue.
I appreciate that maintaining such a toolchain is work, and there is value as well in the diversity and choice of an open ecosystem. But perhaps building is one place where first-class support by the reference implementation creates a worthwhile tradeoff.
half a day is like almost no time at all
half a day is just few hours, how is this a long time .. how is this any time at all
makes me doubt myself a bit, am i too mediocre to think that way
the previous line make more sense to me "It's taken me years to settle into this system" , now this is more like it
Fundamentally venv breaks your conceptions of what a shell is through cleverness, and that's a problem for people who are new.
None of Go, JavaScript, or Rust require a half a day to figure out packaging. Don’t defend Python. Push for it to change or leave it behind.
Trudging through old SO answers, bad docs, GitHub issues, overly enthusiastic blog posts by someone making a todo app, and, the coup de gras: hitting paywalls on the one tutorial that might help you.
Factor in much higher rates for more senior devs and it amounts to a lot of waste.
For individualised shell usage, sure. I have global installations of common data science utilities like pandas and jupyter, or requests.
Reproducibility isn't just about deployment, it's also about coordination with colleagues.
Python needs a cargo. Is Poetry it? I’ve been meaning to try it…
https://github.com/mitsuhiko/rye
This is probably the best package manager I used for Python. It feels a lot like Cargo. It sticks to the standards of Python. No custom lock files ect. Uses prebuilt Python so you don't have to build it. Handles global installs easily.
I dislike this pattern. I don't want every language/tool manager in my PATH all the time. I much prefer the direnv. I hook only direnv into my shell. Then I can do what I need with an `.envrc` in each project directory.
In my opinion, it tries to be far too "magic", hiding everything about the virtual environment and dependency management within it. This means that if anything breaks (and it does break!), I find it basically impossible to fix it without wiping everything and rebuilding the environment from scratch.
I find the documentation nice and clean to look at but somewhat clunky to navigate and very incomplete or not explicit enough — especially for beginners. For a tool that seems to want to be a "one-stop shop", you need to know a good deal already about Python packaging already to navigate it. The lack of explicitness also makes it hard to troubleshoot (as evoked above).
These are by far my biggest complaints, with the below bring mostly minor gripes.
Additionally, it seems to be targeted at library developers rather than applications (a distinction which to be fair none of the Python ecosystem makes, to the detriment of DX). This doesn't usually end up being a big issue as it's reasonably easy to "librarify" applications, but it's something else to learn. (Not every project wants wheelification as its end goal.)
The usage of pyproject.toml is nice, but the semantics of the file isn't quite as standardised as one might hope, especially when it comes to reflecting the package you're writing in the virtual environment. And the documentation around pyproject.toml is scattered and sparse. (A PEP is not documentation!)
I've complained a lot, so I should round off by saying that Poetry is a commendable project and I have high hopes for it; but as it exists now it's just too rickety for me to recommend it. But others may disagree, and by all means I'd give it a try for your next project.
My personal recommendation remains pyenv + virtualenv (or venv) + pip-tools, with simple projects just using a requirements.in files and then moving to pyproject.toml once it reaches a modest degree of maturity.
In the meantime I cant recall any instance in the last five years (at least) where a project would fail to get going based on a simple venv and pip setup.
An exception is CUDA but I think we can agree this is not what you need to setup every day.
I wonder if that benign view due to Linux and people experiencing something very different in other OS's.
This already exists: https://rvm.io/gemsets
Most people don't use it because they haven't felt the need. Bundler already makes sure it loads exactly the right versions of gems for your project, so there's no need to isolate them from each other.
dune init …
opam install some tools like lsp
restart vscode
dune build --watch and I’m good to go.
2. Agree, YAML has decisively won the config format wars. But dune it's extremely unlikely that dune will change this, and we can at least tell ourselves that s-exprs are pretty cool after all.
3. Yup, I hope Eio becomes the de facto standard in the future.
I have in most years learned some nicher languages: Elm, Rescript, Racket, Common Lisp.
But nothing is even remotely bad as Haskell is.
Even just setting up an IDE to have basics like syntax highlight or go to definition is a giant chore.
On the official Haskell devcontainer offered by GitHub is nearly impossible to add any external dependency, so having a one-click shareable Haskell environment in the cloud (the very basic to onboard people on the language without wasting them days into having their own inconsistent environment) is a must.
Another thing that I feel sucks is the default documentation for Haskell libraries. Doesn't say anything useful at all, bundles few definitions here and there, 0 examples, and more often than not there's 0 documentation.
Without much direct experience of cargo I can't say whether that carries across, but I wouldn't be surprised if it was genuinely the best of breed and stays that way.
That's true of most new languages. It seems like most projects these days can produce a LSP with minimal effort, make the obvious choice of sandboxing every project separately to avoid dependency hells across projects, have at least basic distribution systems even with small communities, etc.
The community learned a lot from the mistakes of yore.
The situation frankly really just sucks: as far as I am concerned--from the shear number of bugs I have run into trying to do what should be table-stakes, and which even autoconf handles trivially: doing deterministic builds across platforms of a library to be embedded into another language's build system--cargo optimizes for extremely simple use cases (hell: ones so simple they lead to the wrong mental model of compiling in the first place! like, people seem to seriously think you need to compile for old systems ON old systems using old tools as cross-compilation is somehow treated as esoteric) while throwing the people who know what they are doing under the bus, which isn't at all how one should build tooling; instead, the goal should be to make extremely difficult things easier to pull off, even if it means the simple things have to be a bit harder, as your overall workflow is then easier to manage and you can better grow with your tooling rather than having to eventually throw it away entirely.
Can you clarify what this is referring to?
> the goal should be to make extremely difficult things easier to pull off, even if it means the simple things have to be a bit harder
There are dozens of existing build systems that have this philosophy, and personally I appreciate the default system being optimized for the average use case. The fact that the happy path is so easy is the reason that people overwhelmingly choose to use Cargo; we can lie to ourselves all we like about the appeal of things like memory safety and type safety, but at the end of the day Cargo is the reason that Rust is popular. (And to be clear, I'm not trying to say Cargo is perfect; I have a slew of my own bugs and feature requests filed against it.)
FWIW, maybe that's true for you, but there are numerous other advantages to the language for which many people choose to use Rust--some even "despite" Cargo: you see Google having had to put in way way WAY too much work to get Bazel working for Rust :/--that it honestly feels a bit like belittling an extremely important language to make this claim so flippantly.
In my case, I often feel bad that I'm not using Rust more often, as I'm one of those developer churning out tons of C/C++ code. I do my best, but... I'm a security engineer! I know what I'm doing is stupid! But like, I'm having a hard enough time just dealing with the Rust library dependencies I've accumulated, so I'm not going to start developing even more Rust :(.
> You can set a default build target for a Cargo project with two lines of configuration, no nightly features necessary...
This doesn't work as, as soon as you start setting target-specific options, it infects the host's options, as they incorrectly modelled the problem as some kind of map from targets to flags. If you don't believe me, on your Linux computer, try cross-compile something complicated that will runs on a "least common denominator" Linux distribution, such as CentOS 7.
> Can you clarify what this is referring to?
Sure. I've Googled rust cargo target host bugs for you (which, FWIW, finds a number of bugs I've filed or have talked about, but it isn't as if I have a list anywhere). Note that one of these bugs is "closed", but I still provide them for context as a patch might have been merged but (as you'll find out if you read through all of these) it isn't stable.
https://github.com/rust-lang/cargo/issues/8147
https://github.com/rust-lang/cargo/issues/3349
https://github.com/rust-lang/cargo/pull/9322
https://github.com/rust-lang/cargo/issues/9453
https://github.com/rust-lang/cargo/pull/9753
The result of this work being left incomplete is that increasingly large numbers of "serious" projects--things I'd expect people in packaging land to have heard of, such as BuildRoot--are being forced to set the ridiculous environment variable __CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS="nightly" in order to get access to a flag that makes Cargo sort of work.
I remember a bunch of other ones but I think they are linked from these bugs (I didn't want to dig too long on this); you can also find--either linked from these bugs (as people will tag the bug in their patch) or by searching for that variable (as no one else would use that in a sane project) the projects that are slowly being forced into this crazy workaround.
(What is sad is I often see people surprised at how long it is taking for various of the more important clients to fully get into using Rust, as the safety issues are so severe from continuing to use C/C++; as you made the contention that you believe the reason why people use Rust is Cargo, I will say the opposite: the reason why we don't see more Rust is also Cargo.)
And like, that Cargo doesn't handle this correctly is kind of silly as one of the engineers from BuildRoot seriously broke his back for a year jumping through hoops with them to come up with a compromise and satisfy all of their roadblocks (the Cargo people would rather spend hours talking with people about a patch than spend a few minutes touching it themselves... it was honestly demoralizing, but I've seen the same thing from projects like Flutter, so I think that it is just the reviewer / developer power dynamic that big tech taught a bunch of people) and the code is even in Cargo and yet is rotting in nightly.
(Meanwhile, I often see people bemoan how large projects aren't picking up Rust and using it to slowly cannibalize the C/C++ code in various projects, or are looking at distributions demanding answers for why there has been such struggle getting Rust code distributed, but the answers lie in the build model of the language and in no small part how difficult it is to use Cargo. If you just want to not care about those use cases and want to focus on self-compiled use cases--maybe you are doing web development!--that pick up all of the idiosyncrasies of the build system, then the community should just say that.)
You have to use Xcode. You can try other IDEs but LSP equivalent features are at the most basic level. Xcode is the kitchen sink of IDEs which has led to many negative opinions as it struggles under its own weight. Every other year there’s new UI for things like debugging which is fine but what would be really nice is if the actual debugging worked. Technically there are reasons why you can’t print a local variable sometimes when stepping through a program, but in practice, I do not care and I want to know what the value is.
If you don’t care about the “optional” tooling like the dependency manager and LSP or a linter (which is closer to ESLint versus Rust Analyzer), the required tooling leaves much to be desired. The compiler sometimes gives up when it takes too much time to process a complex type. It would be understandable in some cases but most people encounter the problem when just writing seemingly simple SwiftUI. Error messages and auto fix-it suggestions are improving but still disappointing. I remember when Apple switched from GCC to LLVM and everyone was praising the error messages as a reason to switch.
Swift is actually ambitious. The generics system is world class. It has to support the legacy of a huge ecosystem on multiple platforms. SwiftUI is one of those bets that you might be surprised that Apple can still make. But whenever I fire up that SwiftUI Preview, I am crossing my fingers that maybe I’ll see something instead of an error. Swift lives up to its name in terms of moving quickly and the language design is probably fine, but outside of that, the tooling is still very immature for a decade old language which tons of resources are invested in.
That enthusiasm has since waned when stuff happened like compilation times slowing to a crawl because somebody used ternaries extensively. I still think it's a great language, but the tooling really makes it hard to love
What issues? A lot of problems with Python is due to keeping compatibility with Python 2.0. Implicit string concat bites me fairly often for example, and it has never been useful.
It WAS a long slog yes. But now it's pretty much done. And there really was no way to fix the unicode issue without a big painful transition.
> anything older than a few weeks tends to randomly break with some kind of dependency issue
I absolutely do not have this issue. Maybe you're using libraries very different from what I do? But I do think I have pretty wide interests/projects...
> sometimes requiring multiple installations of python on the same machine which will bite each other in hard to predict ways
I don't know what you're talking about here. Do you have an example?
> I've yet to be able to write something in python and call it 'finished' because it never ever continues to work in the longer term.
I don't have this experience.
That other languages don't?
And that's before we get into the various ways in which python versions and library versions can interfere with each other. You couldn't have made a much bigger mess if you tried. And I actually like the core language. But so many projects I wrote in python just stopped working. I remember having a pretty critical chunk of pygame code written for a CAD system that just stopped working after an upgrade and there was no way it was ever going to run again without rewriting it. That's the sort of thing that really puts me of an I remember it long after. Machine learning code is still so much in flux that it doesn't matter. But hardware abstraction layers such as pygame should be long lived and stable between versions. And that really is just one example.
Anyway, I think asking 'That other languages don't' doesn't really matter. But Haskell (see TFA) is one language that always tried hard not to be successful so breaking changes would be permitted (which is fair). Python tries both to be popular and to allow for major stuff to be broken every now and then and that is very rough on those that have large codebases in production.
By contrast, COBOL, FORTRAN, LISP, ERLANG, C and C++ code from ages ago still compiles, maybe you'll have to set a flag or two but they're pretty careful about stuff like that.
If you upgrade libs then sometimes you need to do some work. I’ve found python libs to be pretty stable though so it’s never too bad.
LOL Javascript enters the room.
To be fair, the language needs that tooling to be practical, and the quantity of it isn't helped by its long history so it isn't necessarily the tool makers that are to blame.
I’ve gone all in on them these days, the feedback from sonarlint in the IDE is usually useful.
To sum up, we realized the default behavior was wrong over a decade ago, but rather than change the default behavior of the tool, maybe add a new version of POM files where sanity is the default (it's a versioned format! c'mon!) or a new non-XML file format that triggers a new mode of operation, they stuck with the legacy behavior, forcing every new project to include a bunch of XML boilerplate just to get the behavior that they realized should have been the default 15+ years ago.
That was the situation last time I used Maven, anyway. I'd love to hear that Maven has since added a new mode of operation that is sane by default, without needing extra plugins and configuration, and doesn't use XML.
I can't comment on Gradle. I've worked on a couple of Gradle projects, and the build files were a mess, but I don't know if they needed to be a mess or it was just bad luck.
In years of Java development, I've seen that only once, maybe twice.
And this was 8+ years ago.
Other build tools in other languages decided to use lockfiles to achieve stable builds with dependency ranges. If your application depends on libraries A and B, and A and B depend on library C, then the build tool can check that the lockfile specifies a version of C that fits the ranges specified by A and B, or, if library C isn't in the lock file, find an appropriate version of library C and add it to the lock file.
But not Maven. Maven's solution is for A and B to declare dependencies on exact versions of library C, and then to pick one or the other depending on many degrees of transitivity separate your project from A or B. Seriously:
"Maven picks the 'nearest definition'. That is, it uses the version of the closest dependency to your project in the tree of dependencies. You can always guarantee a version by declaring it explicitly in your project's POM. Note that if two dependency versions are at the same depth in the dependency tree, the first declaration wins." [0]
So if A depends on C 1.2 and B depends on C 1.5, and A appears before B in your pom file, Maven will bundle 1.2 with your application and not fail the build.
I don't know the history of how anybody ever thought that was a good idea. Anyway, they quickly realized that pinning versions in the build was the right solution after all, but instead of adding a separate lockfile, they decided to make you list all the versions in the project file itself. Which is exactly where you want all your transitive dependencies listed, right in your build file, taking up half a dozen lines each because it's XML, right?
Of course Maven is still happy to fall back its "pick the first nearest" algorithm if you fail to pin one of your transitive dependencies, which means your builds might not be reproducible. My boss (quite sensibly) said our builds had to be reproducible no matter what Maven allowed or encouraged, so I had to write a plugin to check for that.
The real tragedy is that because library publishers no longer use dependency ranges, you get to debug and discover violations of semver yourself. Does library A, which specifies C 1.2, also work with C 1.5? The publishers of library A might know that it doesn't, but they don't publish that fact with their library. Jackson plugins were especially prone to semver-unexpected breakage because Jackson didn't have any stable API for plugins. Jackson plugins typically had to use private implementation details of Jackson to work at all, so they sometimes broke on patch releases of Jackson. Library publishers could have encoded knowledge about this kind of breakage in their dependency declarations, but "best practices" said to specify a single version, so that's what they did.
[0] https://maven.apache.org/guides/introduction/introduction-to...
Libraries don't declare version ranges for their transitive dependencies (like they do e.g. in Ruby), instead they just depend on a specific version. Of course, because two different libraries may depend on the same subdependency but in different versions, you will just get one version and you have no way of knowing whether it will work correctly.
That isn't a problem in 90% of cases, but sometimes it is and you'll notice it when you're suddenly getting ClassNotFound exceptions.
There are some solutions to this, e.g. bigger frameworks like Spring publish BOMs, which are just sets of library versions known to work together, but they don't cover everything.
That's the behavior that Maven itself recommends, so Maven is at least part of the problem.
Maven could also sanity-check your build for these issues by default, but it doesn't; it leaves that up to plugins, which you have to configure (in XML) in your build file every time you set up a new project.
You can do so much with Gradle, but 90% of learning it is figuring out what knobs you need to twist; there's a lot of feature overlap and second-system-effect going on.
- The community leans a lot on configuration over code, and that's annoying. Sometimes, a hardcoded string in your conf could have been a hardcoded string directly in the code.
- Sometimes, dependency injection systems are so abstract that knowing which class is depended on in a specific runtime instance becomes a pain in the ass.
- Your idea just won't load class x, the obvious "clean cache" doesn't work as advertised, and you're back to stack overflow to know which couple of file you need to get rid of. That stuff happens just often enough for you to have a lingering feeling of annoyance, but not often enough for you to remember the exact files.
- Sure, idea is great. But in <other language> I was fine with just vim. The LSP was a great help, not a lifesaver.
- Unit testing is great. Annotations are great. Don't you enjoy that unit test class with a dozen annotations spanning 20 lines just above?
- Hibernate. Spring too, while we're at it. Not sure whether you consider this one squarely fits the tooling box.
Seriously though, the hours spent fighting dependency injection and Hibernate issues alone, when working on a really big Java project, could've been a full-time job.
For maximum fun, I once worked on a large ERP system that started out as a Struts 2/Hibernate 3/Jetty project and had an entire Rails app bolted on using JRuby. Some of the stuff in the JRuby side was injected thru Spring. ActiveRecord had to talk through Hibernate.
But I don't think it's necessarily about the tooling.
And yes, Hibernate is just horrible.
The number of times I actually needed to clean my cache I could probably count on one hand over the last 10 years.
Seriously though what's a decent Java build tool? Hacking on Gradle means having to learn another PL entirely. Maven?
Then say I want to publish a library for others to use from their own java project, how do I do that? I've never actually done it, but that page
https://maven.apache.org/repository/guide-central-repository...
seems awfully complicated compared to say
https://doc.rust-lang.org/cargo/reference/publishing.html
or even the pip equivalent.
Granted, working on a well set up java project is nice, but the setup process is not simple.
What is acceptable?
P.S. For easier publishing, use a different repo e.g. Artifactory instead of Maven Central.
https://docs.github.com/en/actions/publishing-packages/publi...
The steps are roughly: 1. Configure the repository you want to push to. 2. Set up your account with the repository. 3. Configure your credentials. 4. Deploy.
The latter gives us a "config" that's subject to Rice's theorem: it's essentially opaque, with no way of knowing what it will do other than executing it. An example I ran into at work: there's no way to list the dependencies of an SBT project (in order to set up an offline sandbox). SBT provides commands which claim to do that, but config files often append dependencies based on arbitrary logic; e.g. we had some like "when running unit tests, add this mocking plugin"; since "list dependencies" doesn't run the unit tests, that plugin dependency was missing from the sandbox.
Well, the same is true for Maven. I know because I've tried. Plugins can download arbitrary dependencies at execution time.
That's where the point about Rice's theorem falls apart: it applies as much to maven as to Gradle because maven plugins can do whatever they want.
I've written a couple of Maven plugins and you must declare your dependencies explicitely even for those. Pulling in stuff dynamically would be possible but not very clever.
The same approach can be used when depending on third-party jars/plugins which don't fully specify their dependencies: just add the missing ones as extra dependencies of our project. (This happens a lot, where projects have undeclared dependencies on some commonly-used library, and don't notice since it's usually available in a well-stocked ~/.maven2 cache)
I am used to classpath problems with surefire but that it downloads undeclared stuff is new to me. Afair it pulls only in transitive dependencies. Could you give an example?
I am mainly building war files and it turned out to be good practice to explicitely declare any ambigous transitive dependencies. There's even a Maven plugin which shows up classes with different hash during build, https://basepom.github.io/dependency-versions-check-maven-pl...
Gradles own docs say "Well-designed build scripts consist mostly of declarative configuration rather than imperative logic".
When it's up to the programmer to make a script declarative, it's not a declarative language.
Dependency injection and annotation-driven development is “magic happens here” that is hard to analyze when something doesn’t work, and hard to reason about in the sense of building a proof that it will always behave in the correct and intended way.
I don't quite get the criticism against annotations. Metaprogramming and code as data seem to get a lot of love in the context of lisp, python or ruby, but a lot of hate when it happens to be done in Java. I absolutely love the powers that reflection, annotations etc give me.
The difficulty of analyzing dependency injection and "magic happens here" is overstated. Just run the code in the debugger. And if you are having problems with the auto-configuration stuff, you can always selectively disable it and explicitly define the beans you need.
How do you set a breakpoint on an annotation? You can’t, and that’s the issue. You can’t reason about annotations in the precise way you can reason about library calls.
You can set a breakpoint on your normal method and you can see if any proxy object was introduced by the annotation in the call stack and set a breakpoint there. You can also just see the usages of the annotation to see where and how it is being used and set a breakpoint there.
Anyway, going back to my original question, similar criticism is equally valid when you do metaprogramming in lisp/python/ruby. Why are these concerns only raised in the context of Java?
I wish more people did that.
Language stacks are much easier to fact check.
There are plenty of orgs that obviously produce a vital service for society, and they are usually starved of good IT people: health care, social services, logistics, electricity production & distribution, industry, agriculture, emergency responders, etc.
> and don't pay well.
:(
Well, the comp is still high enough to live a comfortable life, I just won't be able to bankroll a political party in the near future.
Any pointers on how to do this? A person that close to me is thinking about entering the High Frequency Trading world, and I would like to give them some alternatives.
Every trade matches two parties who came to the market to trade.
That’s… where they work at now
The two criteria I'm interested in as an IC is how useful the job is in my eyes (this you can know before applying), and whether the company gives me enough independence to be able to make things right. So my interviews focus on a single factor: whether the company culture and organization is conductive to good, productive work I will be proud of down the line.
HLS (the LSP implementation) in particular is a pretty young project, and key compiler improvements which will help it improve are still landing.
You might also want a good testing in the lang or stdlib, and a documentation system.
More modern languages like Rust, where tooling was carefully designed in tandem with the initial language release, fare much better in this regard.
JavaScript’s situation isn’t great, but it’s still easier to work with than Haskell, in terms of hours of frustration.
For me it’s Rust that has really raised the bar for what quality tooling looks like.
I, however, disagree about tooling. Haskell's tooling sucks, but having used several other languages since (python, js, java, rust, elm), most tooling sucks. After this tour, I miss the Haskell toolchain. Sure, cargo is great, but that's one in many, and most older languages don't have this. I wonder whether Rust can escape this fate as it ages. The author also mentions being dismayed by Python, so I guess that's mostly me seeing the glass half-full though.