Responsibilities

Hi. I’m Marc and I am an open source addict  😎 

Hi Marc, welcome! 🙌

That is a reality, in fact. I’m part of this group of people that consider themselves addicts to open source. Big part of our work is created to be shared by everyone, and like Spider-man’s uncle said once… “With great power comes great responsibility”.

But what are those responsibilities we should take care of during our development? What parts of our application should we really take care of and which are more vulnerable over the time? Testing, documentation, clearness of our code, abstraction, extension capabilities… we could talk about them all, and for sure, each one can have enough material for an entire blog or a book.

In that case, I want to expose my personal experiences about what I learned over the time by leading an open source project, several small open source bundles and PHP libraries, and I want to do it by explaining how we should take care of our Symfony bundles or PHP component dependencies.

PHP Component Dependencies

When we talk about PHP components, we talk about framework agnostic packages, only coupled to the language itself, in that case PHP, and to some other libraries. Having said that, we could start by trying to understand how the components and bundles are split in some projects, for example Symfony or Elcodi. Both projects have components and bundles, providing the chance to all frameworks to work with their business logic.

– So do we place all the business logic in components?

– The why of your project is placed in libraries. This will be your service layer, and it should be covered with unit tests.

In regard to dependencies, components should only have dependencies on other components. But how can I discover what packages I really depend on? In that case it’s very simple, so we’re not working with any kind of magic. Because our packages are simple PHP classes, checking the usage of all external classes should be enough to know on which libraries we depend.

In that case, I always write code with use statements, so is much easier to check my external classes usage. Just by checking the first lines of all my classes I can guess what packages I should add in my composer.

This piece of code makes you depend on four packages at least. Please, don’t focus on the versions, but only on the libraries.

Dependencies of Dependencies

Your package must manage ALL of its dependencies, even if they are as well dependencies of your dependencies.

– Your package A uses B and C.

– Your package A requires B.

– Package B requires C.

– Then, your package A has both B and C. Enough.

Well, that’s not true at all, because you cannot depend never of the dependencies of your dependencies. Maybe now they require a package, but maybe this package won’t be required by your dependency anymore in the future. In that case, even if you still need it, your package will disappear from your vendor folder.

– Your package A uses B and C.

– Your package A requires B and C.

– Package B requires C.

– Then, your package A has both B and C.

– Package B does not require C anymore.

– You still have B and C.

Remember that… Require ALL your dependencies. All of them! That can make the difference.

Adapters

If our application is super decoupled from other libraries, and you have used adapters for those integrations, then things change. Because the use of adapters allows you to decouple from other packages literally, we should find another mechanism to say… hey, maybe you can depend on this package…. Composer proposes that mechanism, by using the suggest section.

Of course, this is not the only way of doing that. In this example we assume that our package will offer all the adapters implementing an interface, and this is just an option. In my case, I’ve been working for so long with a library called Gaufrette, and I really enjoy the way these kind of packages work.

Some of you could say… oh, but by doing that, then you’re not defining dependencies but only suggestions (sounds the same as saying nothing, in fact), but when we define a requirement is when our package cannot exist without this package. When it is a MUST

Other kind of implementations don’t take into account the possibility of using the suggest section in composer, because they don’t really solve the dependencies problem. This implementation forces having 1+n packages, the first one containing the interface and the common content, and the other n containing each specific implementation, all of them requiring the first one as a dependency and the specific third-party package.

This is much more heavy to maintain, and only works if you only offer one port with n adapters in your package. In the case of Gaufrette, this could be a reality, so they only offer one port with n adapters, but of course, having this structure is more difficult to maintain.

Versions

That is a very complex topic. I will not talk about composer, but firstly, I will share some basic concepts that are used a lot when defining dependencies between packages.

– 5.4 means equal and bigger than 2.5.4 but smaller than 2.6

– 5 means equal and bigger than 2.5.0 but smaller than 3.0.0

– 5.4 means equal and bigger than 2.5.4 but smaller than 3.0.0

– 5 means equal and bigger than 2.5.0 but smaller than 3.0.0

The only thing I can say about that is that if your library aims to be usable by a biggest community as possible, then please consider checking your dependencies deeply, offering as much version-compatibility as possible.

The following composer requirements…

imagen-5

are more restrictive than the following ones…

imagen-6

Of course, you should add requirement compliance as long as they really cover your library needs. If you use Traits, then you should use ^5.4, or if you’re using some Symfony features introduced in a specific version, then you become dependent, at least, on this version

imagen-7

Consider as well the compatibility with new major versions, as soon as they confirm their roadmap strategy and feature list. In that case, we could add compatibility with Symfony ^3.0 if we have removed all deprecated elements from old versions, or PHP ^7.0 if we don’t use any ^5.6 deprecated function.

imagen-8

This is very important, because if you don’t offer this kind of compatibility, no one using your package will be able to evolve properly, and when I mean someone using your package I mean anyone using any package that, recursively, uses your package.

That can be tons of projects.

Continue: Read part two