(Those 16 years are four years at Google, 10 years in a pair of startups, now 1.5 years at a medium-sized acquirer.)
Correct. These frameworks are synonymous with "Enterprise Java", which you're unlikely to encounter at startups on one end, or Google at the other, but at many places in-between
Agreed, even if my experience has been the exact opposite of yours. I've seen Spring be used in my country a lot, although recently Spring Boot.
On one hand, I really dislike how code is sometimes exchanged for annotations that cannot be debugged with breakpoints easily (unless you dig in to the code where the breakpoint is defined and then come up with breakpoint conditions that are triggered exactly for your code, but not the other 500 places in your app where that annotation is used) and don't allow for easy customization (vs just changing a few lines of code in the actual method body), or sometimes just don't work altogether (e.g. trying to log method execution times with something like AOP, which didn't work with Spring calls sometimes).
On the other, it's better than some of the in-house frameworks that I've seen, with sparse comments written in Lithuanian (which I really don't speak) and just bad in most respects. Honestly, most off the shelf open source frameworks/libraries that are actively supported should be good enough, be it Quarkus, Dropwizard, or something else entirely. Though at least something that's widely known and has lots of questions and answers surrounding common use cases in places like the documentation and StackOverflow. Otherwise, you're in for a bad time.
As a polish-russian lithuanian I do read Lithuanian, russian and bits of polish but... it really is super surprising to see how people abuse unicode-encoded source code. Linux drivers with comments in Chinese are evil!
However, to me it feels like English should remain the "lingua franca" of working in ICT, since most mainstream programming languages are already geared towards English and most of the popular frameworks/libraries also adopt the language, in addition to most of the actual learning materials and books out there. Otherwise we'd soon run into the problem of lots of fragmentation and missing out on useful information.
Or just what we had in Latvia, where a bunch of scholars tried making "Latvian versions" of various English terms, to mostly confusing and useless results, since everyone knows the English ones but very few actually want to use the Latvian alternatives. For example, "DevOps" became "izstrāddarbināšana", which sounds kind of awkward and needlessly long even in our language.
I'm not sure how doctors would work across borders when there would be localized names for over 200 bones that they'd need to learn in each language. Or at what pace software/libraries would move forwards if even changing a simple message would require translations in thousands of languages (which are oddly an order of magnitude more plentiful than we have countries).
As a counterpoint to my own argument, domain code (for a particular system in a particular country) in the local language might sometimes be more convenient to use, rather than developers with insufficient English knowledge choosing the wrong translated terms or even letting typos sneak in. There was this presentation a while ago where someone from Germany I think talked about how the domain code is in their local language and seeing two languages mixed a lot in the same service/class was actually telling of a separation-of-concerns violation, which I found amusing.
English is source code lingua franca, and every reasonable dev should understand this point.
It is kind of more complicated with tech literature though.
But spring-boot has an almost zen like quality once you get that it favor convention over configuration. When I was a Java developer, I'd usually use spring-boot with the following dependencies to make the experience better:
- lombok: to generate the boilerplate: constructors, getters, setters, equals, hashcode...
- mybatis-spring-boot-starter: mybatis is a sql resultset mapper, you write the sql and it maps the results. I find that ORM like Hibernate or Eclipselink are a complexity trap: easy things are really easy but hard things are incredibly complicated, mybatis avoid that.
I couldn't disagree more. I can understand disliking the fact that we need getters/setters in the first place, or perhaps dynamic things like annotations which aren't "normal" code, but static code generation is something that the industry absolutely should embrace.
Model driven development should be more common.
For example, if you can just draw a few ER diagrams and have MySQL Workbench forward engineer SQL migrations for you to check them over (especially if you need to change 20 tables), why shouldn't you take advantage of that? Having a model of your schema that you can generate from the live schema and then transform it into either a set of fresh migrations or just a delta for bringing an older schema version up to date.
I've actually used these two approaches to save bunches of time for personal projects in the past, even though I took the SQL output and put it into dbmate migration tool.
Reverse engineer: https://dev.mysql.com/doc/workbench/en/wb-reverse-engineer-create-script.html
Forward engineer: https://dev.mysql.com/doc/workbench/en/wb-forward-engineering-sql-scripts.html
But databases are just one example. Remember SOAP?Despite being hard to use, one of the best points of SOAP was WSDL - Web Services Definition Language files which allowed you to have a fully functional description of a particular API contained in a single file. Back when REST started replacing it, there was also WADL, but that didn't really go anywhere. The claim that web service endpoints (REST or otherwise) should be largely dynamic/schemaless is basically a lie, because in most languages you'll indeed want to work with particular fields for the objects that you expect to be returned, or domain classes that shouldn't be that different in practice from what you expect.
The beauty of WSDL was that you could get the file from a live version of an API, feed it into something like SoapUI and get a fully working API client, upon which you could build a test suite. Or even better, generate client code for your language of choice, so instead of making HTTP calls and wondering about how to initialize the client, you could start using library code much sooner, with parameters and methods already created for you.
Some time later, OpenAPI came along, but the wisdom of SOAP was basically lost, since it took a while for projects like OpenAPI Generator to pop up and even now the approach of generating client (or even server!) code based on some specification seems to be utterly lost on the industry.
WSDL: https://en.wikipedia.org/wiki/Web_Services_Description_Language
WADL: https://en.wikipedia.org/wiki/Web_Application_Description_Language
SoapUI: https://www.soapui.org/
OpenAPI: https://en.wikipedia.org/wiki/OpenAPI_Specification
OpenAPI Generator: https://openapi-generator.tech/
Want more examples? What about database schemas and mapping them to your ORM/other persistence layer solution? I think if you're writing your own model code, you're doing something wrong, regardless of the language that you use. You'll probably mess up or miss relation mapping with something like Hibernate, will miss out on some comments for autocomplete in Laravel and just generally will have an inconsistent persistence layer that will make you waste time.In most cases, starting with the schema first and using one of the available generation solutions to fill in the application side of the persistence layer seems like the only sane options. Sure, some might prefer to handle migrations in the app side, like Ruby's Active Record Migrations, or something like Liquibase, which are also passable approaches, as long as you don't create a bad schema just so it fits your application.
Java JPA entity generator example: https://github.com/smartnews/jpa-entity-generator
Java generator to get DDL from JPA: https://github.com/Devskiller/jpa2ddl
PHP (Laravel) generator to get models from schema: https://github.com/krlove/eloquent-model-generator
Ruby Active Record Migrations: https://guides.rubyonrails.org/active_record_migrations.html
In most cases, when you integrate two systems, APIs or just bits of code, one side should be the source of truth and the other should match it as closely as possible (problems with data types aside). Somehow the industry doesn't really know how to do this well, though thankfully projects like gRPC prove that it's perfectly doable in a modern setting.When this isn't done, you end up with either technologies that are a bit too green to be used successfully (e.g. picking GraphQL to supposedly deal with dynamic data and then being a month late with actually shipping), or having to waste your own time writing "boilerplate" that you will actually need, because although it could and should be generated, it is actually needed (to query that API, access that database, migrate that schema).
I do agree in regards to the pointless Spring XML boilerplate, which is indeed useless.
Of course it's ambiguous because Java can mean both the language, and the wider ecosystem of libraries, languages, JVMs, tools etc.
Instead, Lombok allows me to mark fields with an annotation and then use getters and setters that never actually exist in my code. In practice, it's just adding an annotation in places to control functionality, which is not what I usually think of when I read "generate boilerplate."
Just as ridiculous is the HttpSecurity method chaining “DSL”.
DSL is a general name to any embedded language inside a host language. C++'s templates can be used to write DSLs, they are compile-time checked. Kotlin's lambda syntax can also create very convincing DSLs[1] to describe hierarchical structures\processes, and they are fully typed, compile-time checked constructs.
There are far more to DSLs than hacky strings (regex) or chaining method calls at runtime (fluent APIs). It's a very general pattern.
[1] Example from https://kotlinlang.org/docs/type-safe-builders.html
html {
head {
title {+"XML encoding with Kotlin"}
}
body {
h1 {+"XML encoding with Kotlin"}
p {+"this format can be used as an alternative markup to XML"}
}
} @Entity
class User {
@NotBlank
String username;
}
// Use site
public User addUser(@Valid User user) { ... }
as opposed to class User {
String username;
public void validate() throws ValidationException {
if (username.isBlank()) throw new ValidationException("username is empty");
}
}
// Use site
public User addUser(User user) {
user.validate();
// ...
} fun calc1(@RequestParam @NotNull @Size(max=10) @Pattern(regexp = "^\\d{4}-\\d{1,2}-\\d{1,2}$") date:String,
@RequestParam @NotNull @Size(max=5) @Pattern(regexp = "^\\d{2}:\\d{2}$") time:String,
@RequestParam @NotNull @Size(max=40) @Pattern(regexp = "^[-_'A-Za-z/\\d]{2,50}$") zone:String,
): Map<String, Any> {
Why stuff everything in the param list? No other framework I can think of proliferates annotations like this. Spring annotations being Java-based means I must suffer Java's ridiculous inability to handle regex metacharacters - even after introducing raw string literals in Java 13. Kotlin handles this perfectly but once I'm in Spring annotation-land Spring's touted Kotlin compatibility goes out the window.It's not even that bad, when you consider something like myBatis, which can be useful for cases where you need complex dynamic SQL for your models, but can also involve boilerplate if you don't have codegen: https://mybatis.org/mybatis-3/sqlmap-xml.html
Though in regards to service code and such, I agree, it can get out of hand and be a total mess sooner rather than later. Not being able to properly debug this magic is just the cherry on the top.
However, defining the validation schema in the parameter list is ugly too. Define it elsewhere and have it run through a validation middleware.
The problem I see is as far Java ecosystem goes Spring is big, respected dev framework to be used liberally anywhere. So if a developer like me call Spring a revolting piece of shit software it is just me asking to become jobless.