That style of programming was also popular back in the pre-OO days with tagged unions in C. It's pretty easy to write fragile software that way that has a lot of incidental dependencies.
It basically makes every single field part of your public API. Once somebody starts consuming that field directly you can never remove it or change it or replace it with a synthetic property.
I'd argue it's better in the long term to change the API than to maintain an increasingly elaborate facade as your data structure changes. OOP doesn't solve the core problem; it just lets you sweep the issue under the carpet until it becomes too big to easily deal with.
It gives you the freedom to make a lot of smaller changes before you have to resort to explicit versioning. I think the benefits of encapsulation are pretty well established at this point.
For example, the entire Apple CoreFoundation API is exposed as operations on opaque C structs. Cocoa uses that abstraction to wrap them in a very nice, high level API. If the actual layout of those structs was exposed I can guarantee you people would start writing code that used that information directly and Apple's hands would be tied when they wanted to improve their implementations.
Where I agree with Rich is that we should work to minimize mutable state.
If the only benefit of encapsulation is to make small API changes easier, is it really worth the additional complexity it adds to your architecture?
A strong theme in many of Rich's talks is that we should prefer "simple" over "easy", as the former will benefit you more in the long run even if it's harder in the short term.
You also seem to be confusing encapsulation with abstraction. Working with data structures directly does not mean you need to operate at a low level.
> Changing software without breaking it is the central problem of software engineering
But encapsulation doesn't solve that problem, except in very minor cases. Is it worth complecting the architecture of our software for the development equivalent of a bandaid?
> In what sense can you have encapsulation when your entire data structure is exposed to every function that touches it?
I'm not saying you should have encapsulation... Did you mean to say "abstraction"?
But encapsulation doesn't solve that problem, except in very minor cases.
The WIN32 API has largely remained unchanged over the last 15 years while changing dramatically under the hood in its implementation. With the recent possible exception of Apple's recent bull run this is so far the most lucrative API in the history of the world. And it's made possible by the fact that all the key API elements are exposed only as opaque handles.
I could go on and on with examples like these that are about as far away from a minor bandaid as you can get.
I'll give an example of what I think Hickey is talking about. In a OO API you have an
class Employee
private boss
public getBoss()
What IMO Hickey thinks, is that you should have instead a map with a field boss. Yes, you can't then change that field name, in the same way you can't change the method name getBoss in the OO API. But you can change the content of that field.
I'm not sure I'd classify Windows as changing dramatically in its implementation, and the cost of maintaining binary backward compatibility has been considerable. Only a company the size of Microsoft could afford to do it year after year.
There's also a difference between building good software and building profitable or even popular software. Sometimes bad design can even help maintain a product's market lead, as if a design is complex, it's hard for competition to make compatible products.
You're also still confusing abstraction and encapsulation. An API can be an abstraction over a lower-level system without being an example of encapsulation.
With web services, documents are your public API. Once a service is public, there is a contract between you and the consumers of the API that it will not change until a new version comes out. This is why we see the /v1/ convention in REST services. /v1/ MUST not change on people. You wouldn't do that with a public C library, you shouldn't do that with web services. It is anti-social to change that API on developers.
These documents are very complex. Just take a look at a twitter timeline JSON response. Having a getter and setter for every field in that document would get ridiculous.
The problem with JSON is it is so easy to change that it takes self discipline to enforce your own social contract. We all know that when faced with deadlines some developers get a little fast and loose to meet that deadline.
But you should not even have a /v1/ in your URLs. mysite.com/v1/people/john is probably not a different person from mysite.com/v2/people/john. Instead, you should have versioned content types. The URLs of a REST service can change as much as they like, the client should not assume any other URLs than the entry point.
The form of the response, be it JSON or something else, is the thing that should not change. And you are right that even with a documented content type keeping the promise is hard.
/v1/people/john and /v2/people/john are different representations of the same internal state.
REST = Representational state transfer. While a URI is a unique identifier, it uniquely identifies a representation of internal state. The structure of the representation under a version should stay constant or else you void your social contract with consumers of your service.
The structure of the representation under a version should stay constant or else you void your social contract with consumers of your service.
Yes, but the version should be stated in the Content-Type header, not in the URI of the resource. URI identifies the resource and Content-Type identifies the representation.
The problem with encapsulation is that invariably you will think something is an internal detail, and "protect" it. Later, I will come along and want to use your code to do something you did not envision, and the "protected" status of some part of your code will greatly complicate my life. I don't have a problem with separate name-spaces for "public" code versus "implementation" code, but limiting my ability to re-use implementation code is just plain wrong.
Ultimately, if someone uses something that is part of a transient namespace, they are responsible if it breaks at a later time. You should not handcuff everyone to save stupid people from themselves; stupid people will always find a way to shoot themselves in the foot.