Show HN: I Wrote a Book on Java
This book is a distillation of everything I’ve learned about what effective development looks like in Java (so far!). It's about how to organize programs around data "as plain data" and the surprisingly benefits that emerge when we do. Programs that are built around the data they manage tend to be simpler, smaller, and significantly easier understand.
Java has changed radically over the last several years. It has picked up all kinds of new language features which support data oriented programming (records, pattern matching, `with` expressions, sum and product types). However, this is not a book about tools. No amount of studying a screw-driver will teach you how to build a house. This book focuses on house building. We'll pick out a plot of land, lay a foundation, and build upon it house that can weather any storm.
DoP is based around a very simple idea, and one people have been rediscovering since the dawn of computing, "representation is the essence of programming." When we do a really good job of capturing the data in our domain, the rest of the system tends to fall into place in a way which can feel like it’s writing itself.
That's my elevator pitch! The book is currently in early access. I hope you check it out. I'd love to hear your feedback.
You can get 50% off (thru October 9th) with code `mlkiehl` https://www.manning.com/books/data-oriented-programming-in-j...
177 comments
[ 3.0 ms ] story [ 77.1 ms ] threadI am sure you (assuming this is your first book) are learning that this is a labor of love, and I wish you the very best in this endeavor. You should be proud!
I was exposed to "data oriented programming" thanks to Clojure—wherein maps/sets are the constructs used to pass data (as plain data) around, with simple functions that work with the data, as opposed to the traditional OO (hello ORM) that mangles data to fit some weird hierarchy.
Java's recent innovations certainly make this a lot easier, and I am glad someone is looking at propagating a much needed message.
I will take a look at the book, but I wish you the very best.
~Unfortunately, I believe they're mutable (and cannot be made immutable).~ Edit: I was wrong, they're immutable.
[0]: I'm using "object" to mean "data bound to methods", since the concept of aggregate data in general long pre-date OOP (eg, C's structs)
[1]: https://docs.oracle.com/en/java/javase/17/language/records.h...
The frustration I have with Records is there is no good way to prevent direct construction of them. That is, the constructor is public, which prevents an easy way of enforcing an invariant during construction.
For example, let's say that you have a record with a Date type. There's no good way to prevent a user from creating the record with an invalid date, one that is out of a needed date range. Or maybe enforcing a field cannot be null or some combination of fields must meet requirements as a group.
The benefit I get from the classic Builder pattern is defeated with Records. I can't enforce checking of my fields before the construction of the record object itself. Presumably I would need to verify the object after construction, which is unfortunate.
(I know very little about Java)
That is factually incorrect.
You can do all of that validation in a record constructor, much like in a normal Java class constructor. There's a difference in syntax: you don't need to repeat the constructor arguments in parantheses, and don't have to perform the assignments yourself. These are tailored specifically for easy validation.
In Python, I sometimes try data-oriented programming, using lists and dicts to structure data. And I find that it does not work well. Once I get two or more levels of nesting, I find it far too easy to get confused about which level I'm on, which is not helped by Python's lack of strong typing. In these situations, I often introduce objects that wrap the map or dict, and have methods that make sense for that level. In other words, the objects can be viewed as providing clear documentation for the whole nested structure, and how it can be navigated.
Author here, I agree with you. I have the working memory of a small pigeon.
The flavor of data orientation we cover in the book leverages strongly typed representations of data (as opposed to using hash maps everywhere). So you'll always know what's shape it's in (and the compiler enforces it!). We spend a lot of time exploring the role that the type system can play in our programming and how we represent data.
I love that talk (and most of Rich's stuff). I consider myself a Clojure fanboy that got converted to the dark side of strong static typing.
I think, to some degree, he actually answers that question as part of his talk (in between beating up nominal types). Optionality often pops up in place of understanding (or representing) that data has a context. If you model your program so that it has "15 maybe sheep," then... you'll have 15 "maybe sheep" you've got to deal with.
The possible combinations of all data types that could be made is very different from the subset that actually express themselves in our programs. Meaning, the actual "explosion" is fairly constrained in practice because (most) businesses can't function under combinatorial pressures. There's some stuff that matters, and some stuff that doesn't. We only have to apply typing rigor to the stuff that matters.
Where I do find type explosions tedious and annoying is not in expressing every possible combination, but in trying to express the slow accretion of information. (I think he talks about this in one of his talks, too). Invoice, then InvoiceWithCustomer, then InvoiceWithCustomerAndId, etc... the world that microservices have doomed us to representing.
I don't know a good way to model that without intersection types or something like Rows in purescript. In Java, it's a pain point for sure.
For example I often really directly want to express is "T but minus/plus this field" with the transformations that attach or detach fields automated.
In an ideal world I would like to define what a "base" domain object is shaped like, and then express the differences from it I care about (optionalizing, adding, removing, etc).
For example, I might have a Widget that must always have an ID but when I am creating a new Widget I could just write "Widget - {.id}" rather than have to define an entire WidgetCreateDTO or some such.
In this case you're preferring terseness vs a true representation of the meaning of the type. Assuming that a Widget needs an ID, having another type to express a Widget creation data makes sense, it's more verbose but it does represent the actual functioning better, you pass data that will be used to create a valid Widget in its own type (your WidgetCreationDTO), getting a Widget as a result of the action.
I agree with this logically. The problem is that the proliferation of such types for various use cases is extremely detrimental to the development process (many more places need to be updated) and it's all too easy for a change to be improperly propagated.
What you're saying is correct and appropriate I think for mature codebases with "settled" domains and projects with mature testing and QA processes that are well into maintenance over exploration/iteration. But on the way there, the overhead induced by a single domain object whose exact definition is unstable potentially proliferating a dozen types is developmentally/procedurally toxic.
To put a finer point on it: be fully explicit when rate of change is expected to be slow, but when rate of change is expected to be high favor making changes easy.
> To put a finer point on it: be fully explicit when rate of change is expected to be slow, but when rate of change is expected to be high favor making changes easy.
I agree with the gist of it, at the same time I've worked in many projects which did not care about defining a difference between those types of data in their beginning, and since they naturally change fast they accrued a large amount of technical debt quickly. Even more when those projects were in dynamically typed languages like Python or Ruby, relying just on test cases to do rather big refactorings to extrincate those logical parts are quite cumbersome, leading to avoidance to refactor into proper data structures afterwards.
Through experience I believe you need to strike a balance, if the project is in fluid motion you do need to care more about easiness of change until it settles but separating the actions (representation of a full fledged entity vs representation of a request/action to create the entity, etc.) is not a huge overhead given the benefits down the line (1-3 years) when the project matures. Balancing this is tricky though, and the main reason why any greenfield project requires experienced people to decide when flexibility should trump better representations or not.
I am in complete agreement, and this is why experienced architects and project managers are so key. Effective software architecture has a time dimension.
Someone needs to have the long term picture of how the architecture of the system with develop, enforce a plan so that the project doesn't get locked into or cut by early-stage decisions long term, but also doesn't suffer the costs of late-stage decisions early on, and manage the how/when of the transition process.
I think we could have better tools for this. Some of them in libraries, but others to be effective may need to be in the language itself.
In TS the `Omit<T, K>` type can be used to remove stuff, and intersection can be used to add stuff
Make and model vs. makeId and modelId: Pick one. Are Make and Model referenced by Cars or not? There seems a slight risk of the Banana/Monkey/Jungle problem here, so maybe stick with ids, and then rely on functions that lookup makes and models given ids. I think it's workable either way.
As for all the optional stuff (color, year, ...): What exactly is the problem? If Cars don't always have all of these properties then it would be foolish of Car users to just do myCar.colour, for example. Test for presence of an optional property, or use something like Optional<T> (which amounts to a language supported testing for presence). Doesn't any solution work out pretty much the same? When I have had this problem, I have not done a proliferation of types (even in an inheritance hierarchy) -- that seems overly complicated and brittle.
In the field of ontology (say OWL and RDF) there is a very different viewpoint about ‘Classes’ in the objects gain classes as they gain attributes. :Taylor_Swift is a :Person because she has a :birthDate, :birthPlace and such but was not initially a :Musician until she :playsInstrument, :recordedTrack, :performedConcert and such. Most languages have object systems like Java or C++ where a Person can’t start out as not a Musician but become one later like the way they can in real life.
Notably in a system like the the terrible asymmetry of where does an attribute really belong is resolved, as in real life you don’t have to say it is primary that Taylor Swift recorded the Album Fearless or that Fearless was recorded by Taylor Swift.
It’s a really fascinating question in my mind how you create a ‘meta object facility’ that puts a more powerful object system on your fingers in a language like Java or Python, for instance you can have something like
which returns something that implements the Musician.class interface if whereWhite your code to work on Musicians, pass Taylor Swift in.
If she's not a musician, your code won't compile.
Particularly, I am not a Musician now but if I learned to play an instrument or performed at a concert I could become a Musician. This could be implemented as
I really did build a very meta object facility that represented objects from this systemhttps://en.wikipedia.org/wiki/Meta-Object_Facility
in an RDF graph and provided an API in Python that made those objects look mostly Pythonic. Inheritance in MOF is like Java so I didn't need to use any tricks to make dynamic classes (possible in RDF) available.
Eg:
Now you can go ahead and order the params whichever way you wish so long as you're explicitly naming them:For now, I use Lombok's @Builder annotation. It makes it much easier to create and copy a record, where non-assigned attributes are set to default.
Example:
It also has a practical toBuilder() syntax that creates a copy of the original record, with some attributes changedHickey makes it sound worse than it is.
Your types are already optional if you're adding constructors for each permutation of all input parameters.
Dynamic typing advocates sometimes miss that statically typed languages don't force you to encode every invariant in the type system, just those that seem important enough.
Or, if you really want to go overboard, you could use a dependently typed language and write functions that only accept cars with a specific combination of fields not being empty. But that's typically not worth the complexity.
Not a problem.
You're just making your life needlessly hard and blaming Java for the problems you're creating for yourself.
This represents, coincidentally, the bulk of the problems pinned on Java.
Everywhere else the problem you described is a variant of an anti-pattern and code smell widely known as telescoping constructor pattern.
The problems caused by telescoping constructors have a bunch of known cures:
- builder pattern (Lombok supports this, by the way),
- the parameter object pattern (builder pattern's poor cousin)
- semantically-appropriate factory methods.
The whole reason behind domain models taking the center stage when developing a software project is that you build your whole project around a small set of types with the necessary and sufficient expressiveness to represent your problem domain.
Also, "explosion of aggregate types" can only become a problem if for some reason you don't introduce support for type conversion when introducing specialized types.
In the interconnected system view, data schemas can change without notice, and the program should be backwards and forwards compatible to a reasonable degree to avoid being brittle.
This is not a problem when we control the whole universe.
I find that Haskell-esque type systems (strongly typed with frequent use of algebraic data types to represent every possible state in _that_ universe) work better for the latter, but are not the best fit for the former, and they often have to add some escape hatches at the boundaries.
Java itself is in a weird cross of this two - it has a reasonably strong type system nowadays, but it’s also a very dynamic runtime where one can easily create their own class at runtime and load it, reflect on it, etc.
So all in all — are you making that Car as part of your universe where you control everything, and it won’t change in unexpected ways? Make a record, potentially with nullable/Optional/Maybe types for the fields, if that makes sense.
If it represents some outside data that you don’t control, then you might only care about a subset of the fields: create a record type for that subset and use a converter from e.g. json to that record type, and the converter will save you from new fields. If everything is important then your best bet is basically what Clojure/JSONObject/etc do, just have a String-keyed map.
(Note: structural types can help here, and I believe OCaml has row polymorphism?)
I've been doing the same thing since the end of the 80s as well starting with Turbo/Borland Pascal, C++, and later any other language that supports OOP.
I think I must be misunderstanding what you mean by that, because I can very much do that in Python.
In modern typed Python, you can instead use dataclasses, NamedTuples (both in the standard library), attrs or Pydantic (both third-party) to represent structs/records, the latter also providing validation. Still, TypedDicts are helpful when interfacing with older code that uses dicts for heterogeneous data.
My main gripe with them is that different TypedDicts are not compatible with each other. For example, it would be very helpful if a dict with x:str and y:str fields were considered superclasses of dicts with x:str, y:str and z:str like they are in TypeScript, but they aren't. They are considered different types, limiting their usability in some contexts.
When using homogenous dicts, you can still use dict[str, T], and T can be Any if you don't want to type the whole thing. You can use any hashable type instead of str for keys. I often do that when reading JSON from dynamically typed dict[str, Any] to dataclasses.
Having clear data types without oop is possible, even in python.
I see people conflate strong/weak and static/dynamic quite often. Python is strong[1]/dynamic, with optional static typing through annotations and a type checker (mypy, pyright, etc).
Perhaps the easiest way to add static types to data is with pydantic. Here's an example of using pydantic to type-check data provided via an external yaml configuration file:
https://news.ycombinator.com/item?id=41508243
[1] strong/weak are not strictly defined, as compared to dynamic/static, but Python is absolutely on the strong end of the scale. You'll get a runtime TypeError if you try to add a number to a string, for example, compared to say JavaScript which will happily provide a typically meaningless "wat?"-style result.
https://langdev.stackexchange.com/questions/3741/how-are-str...
For example, you will find functions where the runtime value of parameters will change the return type (e.g. you get a list of things instead of one thing). So unless we want to throw out huge amounts of Python libraries (and the libraries are absolutely the best thing Python has going for it) then we have to accept that it’s not a very good statically type language experience.
The JS community on the other hand had adopted TypeScript very widely. JS libraries are often designed with typing in mind, so despite being weakly typed, the static type experience is actually very good.
Moreover, for many libraries there are types- libraries that add types to their interface, and more and more libraries have types to begin with.
Anyway just wanted to share that for me at least it's in practice not so bad as you make it sound if you follow some good processes.
I have long argued that such interfaces are doing it wrong. That's what "Special cases aren't special enough to break the rules." in the Zen is supposed to warn about, to my understanding.
Amusingly, I can't call any time where I'd had to deal with differently typed return values in Python, but just recently had to fix some legacy JS code that was doing that (a function that was returning null, scalar, or array depending upon how many values it got in response to a SQL query).
$0.02.
A popular but nonsensical myth:
In real-world usage, Python's "typing" is about as helpful as Javascript's "typing". Plain old C has stronger typing guarantees than Python/PHP/etc.You have a point that strong vs weak typing is not a binary and that different languages can enable a varying amount of implicit conversions in whatever context (not to mention reinterpretation of the underlying memory). But from ~20 years of experience, Python's type system is nothing like JavaScript's - and it's definitely helpful to those who understand it and don't fight against it.
In my experience it's typically people from languages like Haskell that can't see the difference.
My point is that Python's "typing" guarantees allow a caller to call a function with the wrong type, and get back a wrong answer and/or silently lose data.
Strong typing is pointless if the language is unable to actually prevent common footguns, like passing in the incorrect type.
I'm moving more and more to the opinion that arguing about the spectrum of strong <-> weak typing is stupid, because type utility is on the spectrum of static <-> dynamic, with dynamic being full of footguns.
Python does support strong typing, albeit optional, with type annotations and tools like mypy.
If a problem is caused by the lack of strong typing, why not use strong typing?
python was always strongly typed: you could not do 2 + '3', ever. nowadays mypy/pyright will tell you that before runtime, hence static.
Mypy for example can also see the types of the dictionary are supposed to be when you use it just like a normal dictionary.
>learning that this is a labor of love
I underestimated both the amount of labor and the amount of love that would be involved. There were more than a few "throw everything out and start over" events along the way to this milestone.
Clojure definitely had a huge impact on how I think about software. Similarly, Haskell and Idris have rearranged my brain. However, I still let Java be Java. The humble object is really tough to beat for managing many kinds of runtime concerns. The book advocates for strongly typed data and leveraging the type system as a tool for thinking.
>Java's recent innovations certainly make this a lot easier
Yeah, it's an exciting time! Java has evolved so much. Algebraic types, pattern matching, `with` expressions -- all kinds of goodies for dealing with data.
I could be called a "Clojure programmer", because I make a living from an app written Clojure and ClojureScript. While I always appreciated the incredible JVM, I always looked at Java the language with disgust and contempt, interfacing with it only as was necessary, but recent work on Java makes it much more attractive. I was impressed by the functional interfaces, modern design with mostly static methods, JSR-310 (date and time) is absolutely great — overall, Java has improved a lot over the years.
It has come to the point where I gasp might consider writing some Java code :-)
I see that the book is incomplete. I didn't know that early access for books was a thing, very neat. It might be pertinent to note in your post that it's still being written, with an estimated release window of Spring 2025.
I'm very much a "consume it when it's ready" person, so I'll keep this on my watch list.
(edit-clarity)
No firm date for the final publication yet.
https://www.amazon.com/Hands-Django-Beyond-Brandon-2016-03-2...
I had two friends who both wrote separate books on JS. One early book on Angular and the other was about jQuery. Both had a hard time with the critical reviews they received on Amazon and it really dissuaded them from doing any more technical writing.
I love your approach and hope you keep writing and don't let the trolls get to you! Our industry needs more people who have this "soup to nuts" approach and take into account how nearly every language has changed dramatically over time.
Again, congrats and keep writing.
There are few things as intimidating as having the Java language architect review your book on Java (haha). It's a much, much better book thanks to his involvement.
He was very generous with his time and there are some good insights there for aspiring developers, as well as some info about the evolution of Java which may be relevant to the more data-oriented features that have been added in recent times.
[1]: https://www.infoq.com/articles/data-oriented-programming-jav...
Also it being from Manning helps. It's difficult to find good books today, so easy to self publish or get reeled in by some paper mill that banks on people wanting to have a book on their resume. So have to have something to filter out signal in the noise.
https://frequal.com/Flavour/book.html
It describes how to make single-page apps in Java, using the Flavour framework. No plugins, no extensions, and 99.9% pure Java. Plenty of sample code and links to relevant podcast episodes and demos.
> TeaVMFan
Ah, that explains a lot of the questions I had about "modern webapps in Java." Relevant: https://news.ycombinator.com/item?id=25978053 (TeaVM: Build Fast, Modern Web Apps in Java; Jan 2021)
Although I would, sincerely, enjoy hearing what KotlinJS doesn't do that made you want to roll your own framework?
The (likely debatable) list of features in 23 that a.) remain preview/incubator and b.) appear relevant to a work on data oriented Java programming are:
[1] https://mail.openjdk.org/pipermail/amber-spec-experts/2024-A... "So, to be clear: there will be no string template feature, even with --enable-preview, in JDK 23." - Gavin BiermanData oriented programming is about building around the data in your domain. The latest tools are nice, but they're just tools.
I know some drift a bit on their implementations over time, but have not be wholesale yanked out.
Obviously the solution to this is to not rely on these preview functions for production code. The way to do that is to run production in an LTS version of Java. I don't think that's an extreme point of view, frankly.
The new stuff is interesting and cool, and in time, it ends up in the LTS version.
Having lived through Java 5, 6, and 8, these are halcyon times for Java. It's moving VERY fast, and has been for some time.
Are there preview capabilities in the LTS versions? Yes, there are. But they're not under the LTS tent. Don't use them. The demarcation between the development releases and the LTS releases are a smart program to get features out into the world, and set some real lines in the sand for advancement. It helps keep preview items from staying in preview mode for an indeterminate amount of time.
And the two year LTS release cycle for a notoriously conservative eco-system is ample.
String Literals (JEP 326) made it to preview and got pulled.
> I don't think that's an extreme point of view, frankly.
Can't see where I suggested otherwise. I just wondered how the author was handling all the "in-flight" (An OpenJDK term there, not mine) features that Java currently has outstanding.
> It's moving VERY fast, and has been for some time.
They've been fast at creating new preview features. Actually landing finalized features though? In my view they're taking too long. When I compare Java and Python in this respect -- and I do as a working programmer in both on a frequent basis -- Java is still slow.
I feel this as well, but I also think it's desirable. Java is slower to add features because the bar is quite a bit higher (especially with regard to backwards-compatibility).
I'd much rather have long previews and occasional removal of previews than have a language that becomes bloated and kneecapped by past rushed decisions.
There's Kotlin, Scala, Groovy, etc, if you want to run on the JVM with languages that offer more features (and footguns). I find the balance OK, personally.
I'd much rather them pull the `STR.` templates than push it forward knowing its not ergonomic in practice.
Also, Java has gotten an insane number of new, significant features in the last couple of years — loom, algebraic data types, pattern matching, ZGC.. I don’t want to disrespect Python, but I really don’t think they have done anything to this degree.
10 years.
In my experience immutable data simplifies a lot of things and avoids a lot of common problems related to concurrency. Implementing efficient copy-on-write isn't too hard when everything is immutable.
>simplifies a lot of things
Runtime immutability simplifies nothing.
>avoids common problems related to concurrency
While simultaneously creating new problems. Like fine, you cannot make an edit on an object out from under the rug of another thread… except that in most cases, you have now just made that old object into invalid state that is lingering around your program like a death trap and to get around this, you are forced in to some pretty terrible synchronization bullshit that just locking objects doesn’t suffer from.
>Implementing CoW is not too difficult
The single greatest predictor of defects for every single language ever is “lines of code”. Implementing CoW sounds straight forward, but actually, incorrect CoW is a common source of bugs and you are just hand waving that away.
Orders of magnitude slower? Citation needed. I don't think I've ever encountered any performance problems caused by immutability in anything I've worked on. But as always, there are tradeoffs, and if there's a bottleneck then it can be addressed when identified.
> The single greatest predictor of defects for every single language ever is “lines of code”.
Did you hallucinate whatever you think you replied to? At no point did I mention LoC. I mentioned simplicity. Two very different things.
You are reading aggression in based on the fact that I am disagreeing with you. It is text over the internet. Whatever tone you are gathering is purely imagined. I would suggest you visit fewer circle jerk areas and discuss things. People telling you you’re wrong and why is not aggression.
>source
I mean. I assume you’re a developer as you have a strong opinion on immutability. So just test it? It isn’t as if testing the impacts of immutability of performance is a difficult thing to test out.
It is actually difficult to find results because functional programmers have flooded the internet with “yes performance is impacted, but performance doesn’t matter. Performance is a premature optimization”
Nevertheless, even with things smaller than a register, performance is impacted here:
https://product.hubspot.com/blog/immutability-and-performanc...
By several X. Now get it objects that are larger than a register with numerous stack blowing deep copies and the effects are massively more pronounced.
The fastest Haskell game engine looks like PlayStation 2 on modern hardware.
I mean. That immutability massively hurts performance is not a point of contention, even among immutability fanboys.
>did you hallucinate
I think you forgot your own post. You stated that CoW is trivial and I gave you a specific reason as to why that claim is a lie.
What really happened is that I was full in on Scala and runtime immutability because I read the plethora of medium articles pushing this stuff and I believed it.
Then I decided to use the stuff for myself and not a single claim that the runtime immutable crowd made panned out, and in fact, in many cases I found myself hard constrained in ways that fucking sucked (threading being a major one).
Then when I took a more practical approach of filing runtime immutability under “tools” rather than “rules”, I stopping having major issues with data, stopped running in to bullshit productivity walls, and stopped running in to minor refactors automatically being major ones, and overall feature implementation became easier and faster.
The only difference between me and the people pushing these things is that I actually decided to ask myself if the supposed benefits were actually panning out, and they didn’t.
Over a few years I became tired of having the exact same stupid bullshit claims without evidence relentlessly pushed by zealots and just gave up with positive engagement with them. It doesn’t matter how much you ask for these people to provide the metrics that back up their claims, they never will. They will give bullshit anecdotes. They will write intentionally bad code in other paradigms. They will lie and lie and lie and lie some more.
All that is to say that I am fully open to DoP being proven. They just have not done so.
One of my personal photos I keep around was taken long ago in what was the biggest bricks/mortar bookseller. I was looking at the selection of books on Java available at the time. O'Reilly was the dominant publisher, and thus had several offerings on the wall. Most of the books were at least 2 inches thick. (If you were ever involved with writing a technical book in the early 2000s, you'll understand the publisher metrics at the time were based on the width of the spine on the shelf.)
Among the many Java manuals of significant girth was a small, THIN book with the title "Java -- the Good Parts". :-{}
there's another related book from one person active in the Clojure ecosystem. Though the book examples are in JS.
also, thank you for taking the step forward on doing your own small part in changing the 'AbstractFactory' thinking that's pervasive in the Java world.
https://www.manning.com/books/data-oriented-programming
A confusing sentence I noticed in the first chapter:
> ...then the only thing cost was some time, if they do it wrong, the cost is usually a bug.
I am guessing you mean "only cost was some time" (without the "thing")?
As for the topic, my hypothesis is slightly different — adopting functional approach to programming — even in imperative languages — leads you to the best patterns (or as you put it, "makes it inevitable") when combined with "evolutionary" architecture, and DoP is certainly one of them.
However, for a majority of software, in my experience, data "attributes" are really "leaf nodes", only to be consumed for display, and types do not really matter much there (eg. I don't mind `firstName` being a simple string). What we want to get right is types we do operations on, and most critically, relations between different data models. Accepting "evolutionary" principles in architecture also means that you welcome change and build for it, so getting any definition of data right from start is not an imperative.
But the topic certainly seems intriguing, so I look forward to learning more from your book and seeing how you apply it in a more imperative/OO way and what language features you found critical to success there.
Congrats again and good luck!
> ...no bad states to defend again.
Defend "against", I guess?
Thanks for pointing out the typos and wonky wording! Will fix!
The first chapter is pretty nice. Record types, switch statements and other new features will hopefully push Java programmers to think in terms of types more often.
There's another book by a similar title
https://www.manning.com/books/data-oriented-programming
Care to elaborate how yours is different?
This modeling difference has pretty far reaching implications. They lead to very different kinds of code bases, and thus very different books.
Here are the repos for the two books. Poking around those should give you a good overview for how radically two things both called "data-oriented" can differ :)
* https://github.com/chriskiehl/Data-Oriented-Programming-In-J... * https://github.com/viebel/data-oriented-programming
I Lost T