Posts in "Philosophy"

Vectors as ADT

I talked about autognostic objects a couple of weeks ago, and in that post contrasted them with abstract data types (ADTs). I promised to follow up with a post on an ADT implementation, so here it is.

First of all, let’s state the autognosis property once again: an autognostic object can only have detailed knowledge of itself. This constraint is required for objects, but not for ADTs. On the contrary: ADTs are allowed (maybe even expected) to inspect detailed information from other values of their own type (and only of their type).

From that point of view it means it’s perfectly fine to implement the Vector add operation in an ADT as follows:

As you can see, we blatantly access the private data (the x and y) of the addend in order to perform the calculation. We can do this because both the augend and addend are of type Vector and ADTs are allowed to access each others private data when they’re of the same type.

The name Vector denotes a type abstraction. With this kind of abstraction, the abstraction boundary is based on a type name (Vector). This means that as a client all you can see is the type and operations, but the implementation is hidden. “Within” the type, though, you have full access to the implementation and representations. It also means that, contrary to objects, you cannot easily interoperate with other values, since they have a different type and therefore have a hidden representation. All ADTs are based on type abstraction.

This also has some implications for extensibility; specifically that an ADT has to know all possible representations. To see that, let’s say we again want to add a polar representation for the Vector. We do this so we can keep the full accuracy when creating a vector based on polar coordinates, accuracy that would have been lost if we’d convert it to rectangular coordinates first. In JavaScript, we can implement that as follows:

It isn’t pretty, but in languages that have sum types and static typing it tends to work a bit better.

The important thing is that we significantly had to change the ADT to support the new representation. In fact, every new representation will require changes to the ADT. Compare that to objects, where we were able to add new representations without changing any of the existing representations. The reason being that ADTs are abstracted by type, while objects are abstracted by interface.

In general, ADTs are much less suited to adding new representations than objects are. It turns out this difference in extensibility is at the heart of the differences between ADTs and objects, and I’ll dive into that further in a future post. Don’t think that all is bad with ADTs though, they have other qualities… If you’d like a sneak peak, check out the Expression Problem on Wikipedia.

Autognostic objects in C# and Javascript

William Cook talks about the difference between objects and Abstract Data Types (ADTs) in this great paper: On Understanding Data Abstraction, Revisited. In his treatise on objects, he lists a property called “Autognosis” which, in his words, describes the contraint:

An object can only access other objects through their public interfaces.

Put another way (again by Cook):

An autognostic object can only have detailed knowledge of itself.

I find this constraint particularly interesting, because it’s something that’s easily violated in conventional OOP languages. Presumably because it’s so easy, a lot of OOP code also actually does violate it.

Let’s take the example of a 2D vector; a reasonable implementation in C# (I’ll come to Javascript later) might go something like this:

So what’s the problem? Well, to compute the sum of the two vectors the augend needs access to the private data of addend (addend._x and addend._y). Thus, one object (the augend) accesses another object (the addend) through something else than the public interface. This violates the Autognosis constraint.

In Javascript, we can have an equivalent implementation:

Whether the autognosis property is violated in this case is a little less clear: Javascript doesn’t have access modifiers, so it isn’t clear from the code itself what is part of the public contract and what is not. If x and y are part of it, it isn’t violated. For now, however, let’s assume only fields that have a function value are part of the public contract. So let’s fix it:

By itself, this doesn’t bring us very much, we’ve just wrapped the instance variables. However, from an extensibility point of view this has brought us a great lot: we can now create new, interoperable vector implementations:

And their interoperation:

Let’s take a moment to appreciate what we did here. We were able to introduce a new vector implementation and use that seamlessly within our existing code, without needing to change any of that existing code. This is great, since it means that your application can be extended and improved after writing it in the first place, without needing to modify it.

There is another great advantage to this: besides you writing new extensions, other people can do that as well, allowing your application to do stuff that you never imagined in the first place. Arguably, the vector example doesn’t really show those traits, but OO design principles is exactly what’s at the heart of platform ecosystems such as iOS and Android Apps, or even the internet itself.

To summarize, that’s one of the great benefits of OOP: objects provide for autonomous extensibility with interoperability. Autognosis is one of the key facilitators of that property.

Afterthoughts

This story isn’t specific to Javascript. In C# or Java we can use interfaces to obtain similar results. We should be careful not to explicitly test for class type in the implemention though, since that again violates the autognosis property. (The same goes for Javascript, but testing for types seems to be less common there)

Also notice that nowhere in this post I either talked about classes or inheritance, yet still arrived at some very useful properties directly related to OOP. This strengthens my beliefs that classes and inheritance are not fundamental parts of OOP.

I haven’t said too much about ADTs in this post, however I’ll try and do a follow up post to explore how you’d do a similar extension using ADTs.

Credits

Inspiration for this post came from:

My 16 definitions of Microservices

If you’re in software development these days it’s almost impossible to have missed all the buzz about microservices. The first time I heard about it was in a presentation by Fred George, which I really enjoyed and was mind-challenging for me. After that, I heard the term popping up more and more and noticed that there isn’t really any consensus on what it means. This really confused me since a lot of people are talking about different things but are calling it the same. However, they all do seem to revolve around the idea of partitioning a large body of code into smaller parts. The question then becomes: on what criteria are you partitioning? Or, what defines a service boundary?

I think that’s the essence of my confusion: people partition their code across different dimensions. So, to clear up my own mind, I’ve decided to compile a list of partitioning dimensions/definitions I’ve come across and share them here with you:

#1 Deployment unit

A microservice is a unit of deployment. By this definition every part of your system that you individually deploy is a microservice. For example: a web site, a web service, a SPA, background tasks, etc. This definition is usually related to the scalability properties of microservices: the idea that you can individually scale different parts of your system, something that’s useful if different parts are under different loads. The services are not necessarily independently deployable.

#2 Independent deployments

By this definition a microservice is a piece of your system that you can (and should) deploy independently. This consists of at least one deployment unit. You are independently deployable if you can deploy your service without other services needing to deploy as well. A service will consist of more than one deployment unit if the individual deployment units must be deployed together for the service to keep functioning correctly. In this approach services are autonomous in the sense that they can be updated independently without depending on other services.

#3 Business function

Each service is responsible for a specific business function. Business functions like billing, sales, pricing, recommendations, etc. This is mostly useful if it’s combined with Independent teams (#5), Separate code base (#15) or Independent deployments (#2). With Independent teams, it’s clear for the business owner who to talk to when there’s a problem or he needs a new feature. Separate code bases help because the discipline of not using raw data from a different business function is enforced since that code is simply not nearby.

#4 Technical function

Here, a service is defined by its technical function such as front-end, back-end, database or messaging infrastructure. As with Business function (#3) this would usually not be considered a microservice without Independent Teams (#5), Separate code base (#15) or Independent deployments (#2). Yet, I’ve seen people calling a good old 3-tier architecture a microservices architecture without there being any other criteria.

#5 Independent teams

Each team is responsible for one service. In essence the code a team is working on defines the service boundary. The team develops and runs the service completely by themselves (2 pizza team). They can work autonomously in the sense that they can make all the decisions with regard to the service they are responsible for. When they have a dependency on another service they agree on a contract with the team responsible for that service. Teams are usually aligned to Business function (#3) or Technical function (#4) and sometimes also have their Separate code base (#15).

#6 Private memory space

Services are defined by their ability to run in their own memory space. If an application runs in its own memory space, it’s a service. Such services cannot be crashed by other services running in the same process (since there aren’t any). Also the in-memory data is completely private to the service, so can’t be used by other services. Each service can potentially be built on a different platform or programming language (#10).

#7 Independent database

Each service has its own private database. Services can’t access databases from other services. There are as many services as there are databases in the system: a database defines the service. The services are completely autonomous in their choice for data storage, schema refactorings, etc. They can also be held completely responsible for the conceptual integrity of their data. In general a service will only have a few tables (or data schemas if you like). This is important because a big problem in large monolithic is the ease of access to data that you conceptually have no business touching. If you’re providing a CRUD-y interface on your service, this doesn’t count.

#8 Temporally decoupled

A service is a piece of code that’s temporally decoupled from other pieces of code. This means a service can keep operating (for a finite amount of time) even if services it needs to interact with/depends on are down. This usually implies some form of async messaging using queues or service buses. RPC between services is out of the question because you seize to be functional whenever the other service is down.

#9 Communicating via REST/JSON over HTTP

A microservice is any application that communicates to other microservices via REST/JSON over HTTP. This specifically discounts the possibility of multiple services running in the process or using some form of binary protocol. This is mostly done from an interoperability standpoint since such a service is highly interoperable as a lot of platforms speak REST/JSON and HTTP.

#10 Independent choice of platform/programming language

Two microservices are not two microservices if it isn’t possible to write them in a different language. In this sense, it’s possible for each service to “pick the right tool for the job”. Service boundaries are defined by the ability to do this.

#11 Objects

Microservices are no different from “real objects”. With real objects being the way Alan Kay originally thought of them: objects providing a goal-based interface to provide some sort of service to the user. Interactions between objects will generally occur through messaging with a synchronous in-proc call being a specific kind of message, but not the only way objects can communicate (though that’s the only way that ended up in OOP languages).

#12 Containers/Docker

A microservice is any application that runs inside a container such as Docker. When people define it this way there are usually other contraints as well (specifically Independent deployments (#2) or Communication via REST/JSON over HTTP (#9)), but I’ve come across definitions that seemingly don’t require anything else but containerization.

#13 SOA done right

SOA was originally intended to do a lot of stuff that’s now attributed to microservices. However, SOA somehow ended up being largely synonymous with web services, and people that were once big on SOA are now happy to have a new name for the same ideas with microservices. The definition of a service in SOA is also not that clear, but I think (correct me if I’m wrong) it was mostly supposed to be about interoperability and IT/business alignment.

#14 Shared-nothing architecture

Services are pieces of code that “share nothing”. That means they’re very well encapsulated: no conceptual or physical data sharing. Combines Private memory space (#6) and Independent database (#7). For some people also means no sharing of virtual or physical machine to run on.

#15 Separate code base

A service is defined by the code base it’s in. Or equivalently: each service has its own code base. This is absolutely required for Independent choice of platform/programming language (#10) but it’s also implied in many of the other definitions.

#16 Bounded context

A service is the isomorphic with a DDD Bounded Context. This means services and BCs map one-to-one. It will usually involve the strong code and runtime isolation found in some of the other definitions, to enforce the models staying clean. Eric Evans also did a talk about this at DDDX.

Conclusion

I think of the above list more as axes than definitions. In fact, I find it highly unlikely that any architecture that calls itself a microservices architecture will conform to only one of the points in the above list. Two or three will be much more likely.

So what is the right definition? I really think it doesn’t matter. All of the above definitions have pros and cons in given contexts. The question is not that much which definition to use, but what context you’re in. You are probably not Amazon or Netflix, so things that apply to them might not apply to you. Quite the contrary, using their definitions and playing by their rules will probably hurt you more than it will help you. Therefore, pick the definition that helps you with the problems you’re facing, be it scalability, time to market, quality or whatever, but don’t get distracted by anything else just to be on the microservices bandwagon.

Closing remarks

I’m sorry I haven’t provided a list of sources with regard to the definitions. Most are straight from the top of my head, and come from blogs I read, talks I’ve seen or people I’ve talked to. If you disagree and think microservices are well defined, please let me know. Also, if I missed any definitions, don’t hesitate to comment.

 

Liskov substitution principle is not for objects

The Liskov substitution principle (LSP, the L in SOLID) relates a type and its subtypes using the following definition:

What is wanted here is something like the following substitution property: If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T.

Which is often interpreted as “at any point in a program where I use an object of type T, I can safely use an object of type S”. In this context, safely means the program can use S without it crashing or causing otherwise undesirable behavior. This interpretation, however, is less stringent than Liskov’s definition, which says: “the behavior of P is unchanged when o1 is substituted for o2”. A behaviorially unchanged program to me means there’s no functional difference, or no difference in output/side-effects (this does allow for differences in space/time requirements).

To give an example of objects that do conform to LSP, let’s look at an abstract data type (ADT), such as a set or a stack. Obviously, there are many ways of implementing a set. However, from the point of view of a program using a set, it shouldn’t matter which implementation you use for the program to behave correctly. If the set implementation attains this property, it has conformed to LSP.

When doing OOP, however, changing the behavior of the program is often exactly what you want. Take the ubiquitous UI Widget class hierarchy example. In this case, you subclass a base Widget in order to get the information specific to your application onto the screen. Here, different behavior for the program using the Widget classes is the entire goal of this design. This means though, that with regard to LSP, the subclass is not a subtype of the base Widget, nor should it be. The subtle point here is that there’s a difference between subtypes and subclasses. A subtype is defined by LSP, but it says nothing about sublasses. However, when talking about LSP in SOLID, LSP is often explained in terms of subclasses instead of subtypes, and this is wrong as this example illustrates (unless you think the design is actually bad).

LSP, therefore, is not a general principle to live by when doing OOP. In fact, a lot of programs/designs are useful exactly because they violate it. OOP is much more powerful than just implementing ADTs, and we shouldn’t apply constraints that hold for ADTs onto objects. That being said, you should make sure that you don’t violate any implicit or explicit assumptions made by the clients of the base class, which I assume is the underlying reason LSP made it into the SOLID principles at all.

Credits

I already had these concerns when first reading about LSP a couple of years ago, however I only thought about writing a blog post about it when I watched a presentation by Kevlin Henney (at around 33:00) a couple of weeks ago where he talked about the same subject. Also, my thinking on objects vs ADTs is influenced greatly by “On Data Abstraction, Revisited” by William Cook and “The power of interoperability: Why objects are inevitably” by Jonathan Aldrich, both are excellent reads if you are interested in OOP theory.

Exploring the essence of object-oriented programming

Warning: no actual answers are given in this post 🙂

For a long time I’ve been fascinated what object-oriented programming is really all about. I’ve never talked to someone who could give me a closed definition, or could even give me the reasons why OOP is a good (or bad) idea. Lately I’ve been doing quite a lot of research, which led me to things as The early history of Smalltak, Smalltalk itself, a lot of work from Alan Kay, the Viewpoints Research Institute, Lisp, COLAs, Meta II and a lot more stuff. Very interesting, I can tell you, but while I learned a lot, I still don’t have the answers. Nevertheless, I thought it would be time to write down my results/thoughts so far.

For a lot of people, OOP is mostly about classes, inheritance, polymorphism and modelling nouns from the real world. When you go back in history, however, you’ll find that the term object-oriented programming was first used by Alan Kay, and he doesn’t seem to mean the aforementioned things at all. Instead, he says the most important idea about OOP is messaging, and that the term OOP was actually not that well chosen since it puts the focus on objects instead. He also says:

OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things.

I’m specifically interested in this “definition”. Why? If a visionary like Alan Kay finds this important, it probably is. And I like to know important stuff 🙂 So what do these 3 things mean, what problems do they solve and how does OOP relate to them. Spoiler: I don’t have (all) the answers yet. Let’s visit these three concepts, and how I understand them at the moment.

Messaging. Communication between objects is done through messaging only. Each object decides how to interpret and act on a message themselves. Objects need to decide how messaging works up front.

Local retention and protection and hiding of state-process. An object has private memory which no other object can access. If another objects wants access to some of the data of an object, this needs to happen through a message. An object itself is in control of actually granting that request.

Extreme late-binding of all the things. This is the one I have the most trouble with grasping the consequences. The idea is to bind everything to concrete stuff as late as possible. For example: compiling to bytecode binds your code to a particular machine later than compiling directly to machine code. The main idea here is that you can delay important choices until you have better understanding. In C# a lot of decisions are already made for you (everything is an object, no multiple inheritance, etc) and we bind early to those decisions since there is no way to change them later. If we later find out multiple-inheritance would help in some part of our code, we have no way to do that. The same goes for selecting which method is gonna interpret a message, in C# this is determined compile time which forces you to do all kinds of weird stuff should you actually want to do that runtime.

From a software development point of view, I’ve seen all three things being helpful in one context or another: it feels logical that combining them indeed yields a very powerful programming model. Why Alan Kay thinks these 3 specifically are that important, however, I’m still not sure. At one point, he talks about Real Computers All The Way Down (RCATWD), meaning that every object can represent anything. That’s powerful, and I currently think it’s related to that, but I’m not exactly sure in what way yet.

One thing I noticed on reviewing this post is that at no point I’m talking about programming languages. That’s probably not coincidental, in the sense that OOP is not tied to any particular language; you can actually do OOP in a non-OOP language, and you can program in a non-OO style in OO languages.

So what is OOP? It seems safe to say that fundamental elements of this programming style are at least objects and messages, where objects communicate with each other through the messages. Objects have a private memory to store the data they need to carry out their computation. And that’s where things get messy: does everything need to be an object? If it is, how do you actually represent data (something like church numerals?)? If it’s not, where do you draw the line? Does it really matter, or is it fine to do just parts of your system OOP? How does functional relate to OOP?

All kind of questions I don’t really have an answer to at the moment, but I’ll get back to you if I find out..