Don’t Hard-Code Yourself. Make your Code Framework-Agnostic

in Backend, Culture by Kamil on January 5th, 2015

When working on a project, we use a framework. Let’s admit it - we don’t write code from scratch (I’m not talking about short, "few-hours" projects). We have a framework we like, know and are used to use frequently.

This framework enslaves you. Why? Because it offers so much shortcuts / methods / helpers you can use instantly, without any effort. You don't have to write any additional code, no new libraries, no new getters or anything. You just use it and, hey, it works!

Why is using framework-given functionality a bad thing?

I'm not saying it's actually a bad thing. You should use it because this functionality is (probably and hopefully) tested well enough, the code is efficient and it will speed up your work. The thing is, it makes you lazy. And this is a bad kind of "lazy". It binds the code to a specific framework. Take a look at the code below:

namespace News;

class NewsController extends \FrameworkController {
  
  public function getRecentNews() {
    return \View::make('news.recent', array(
      'news' => \News\Model::get()
    ));
  }
}

Let's say the client want's to use the same functionality in a number of other places on the website. You can copy the code and paste it into other controllers - it's fast and simple. But after a while the client wants you to change the sorting of the news. Now that's a bit of a problem, since you have to alter all occurrences of this code.

We can go on and on multiplying the client's demands. The real problem is the shortcut, that was made.

How to fix this?

Now, take a look at the code below:

namespace News;

class NewsController extends \AppController {
  
  protected $viewFactory;
  protected $newsRepository;
  
  public function __construct($viewFactory, $newsRepository) {
    $this->viewFactory = $viewFactory;
    $this->newsRepository = $newsRepository;
  }
  
  public function getRecentNews() {
    return $this->viewFactory->make('news.recent', array(
      'news' => $this->newsRepository->getRecent()
    ));
  }
  
}

class AppController extends \FrameworkController {
  
  // ...
  
}

It provides the same functionality with some additional features:

  • it does not rely on any framework,
  • it utilizes Dependency Injection (testable, mockable code = win),
  • its behavior can be easily altered.

Let's examine the code step-by-step.

1. AppController instead of a FrameworkController. If you use an additional layer for controller classes you can easily adapt your code to any framework. This makes your code portable. It also gives you the ability to move some commonly used functionality to the AppController. Now, moving from one framework to another should be easy since you have to adapt only the AppController and not your actual controller logic.

2. By injecting class instances into other classes you gain flexibility. If you want to change the behavior of a specific method (e.g. $newsRepository->getRecent()) you don't have to alter any code in the controllers. You only need to change the getRecent() method. Furthermore, this code is testable - you can mock the injected objects! This is a topic for a different article :)

One more step - type hinting

You can do one more thing to enhance your code. Since it now allows injecting class instances you can type hint classes that can be injected.

Instead of:

public function foo($bar)

You can use:

public function foo(\Baz $bar)

This means that the injected object $bar has to be an instance of \Baz. This is great because you can restrict users to use a certain class for injection. But let's consider this situation:

protected function fetchLog(\VCS\Driver\Svn $log)

You have a method that fetches the commit log from an SVN repository. But time moves on and you switch to GIT. So, obviously, you want your code to support GIT along with SVN. And we have a problem since the fetchLog method accepts only an instance of the SVN driver class. You have to change one or more classes so they will accept GIT drivers. But then again you lose the SVN functionality. Crap.

The best answer for that is interface type hinting. Take a look at the following code:

file: log.php

protected function fetchLog(\VCS\Driver\DriverInterface $log)

file: VCS/Driver/Svn.php

namespace VCS\Driver;
class Svn implements DriverInterface {}

file: VCS/Driver/Git.php

namespace VCS\Driver;
class Git implements DriverInterface {}

This means that the object injected into the fetchLog method has to implement an interface called DriverInterface. Interface type hinting does not force you to use a specific class, so you can inject anything you want as long as it implements this specific interface.

Protip: you can mock an interface so it pretends to be an instance of a class that implements the desired interface. So it's testable! :)

Summary

With only few things in mind you can make your code portable and framework-agnostic. It will be also flexible (interface type hinting, altering one method instead of its dozen occurrences) and testable (injecting mocks). This will be rewarding in the long run.


← back to the blog

You May Also Like

5 Mistakes Most B2B Startups Do in SALES and How to Fix them

by Damian in on October 19th, 2015

B2B sales is hard, but with the right mentoring you can become a hero and increase your business results. Read 5 tips that will push you beyond boundaries and make a better salesman.

Why Should Clients Spend Some Time with the Software Provider?

by Damian in , on January 5th, 2017

Crafting a good piece of software requires patience and trust. It’s an intensive process that brings people together. Before the start of each IT project, it’s worth spending some time with the client and knowing each other.

Wolves Summit 2015 - Sillicon Valley Comes to Gdynia

by Damian in on October 25th, 2015

The first edition of the conference about sales, marketing and investors for startups - next test for Conventica, our event app: passed!

Interested in Creating a Successful Project?

Contact us and together we'll bring your ideas to life!