Upgrade Your Drupal Skills

We trained 1,000+ Drupal Developers over the last decade.

See Advanced Courses NAH, I know Enough
May 03 2021
May 03

Drupal projects can be challenging. You need to have a lot of framework-specific knowledge or Drupalisms. Content types, plugins, services, tagged services, hook implementations, service subscribers, and the list goes on. You need to know when to use one and not the other, and that differs from context to context.

It is this flexibility and complexity that allows us to build complex projects with complex needs. Because of this flexibility, it is easy to write code that is hard to maintain.

How do we avoid this? How do we better organize our code to manage this complexity?

Framework logic vs. business logic

To start, we want to keep our framework logic separate from our business logic. What is the difference?

  • Framework logic - this is everything that comes in Drupal Core and Drupal contrib. It remains the same for every project.
  • Business logic - this is what is unique to every project—for example, the process for checking out a book from a library.

The goal is to easily demarcate where the framework logic ends and the business logic begins, and vice-versa. The better we can do this, the more maintainable our code will be. We will be able to reason better about the code and more easily write tests for the code.

Containing complexity with Typed Entity

Complexity is a feature. We need to be able to translate complex business needs to code, and Drupal is very good at allowing us to do that. But that complexity needs to be contained.

Typed Entity is a module that allows you to do this. We want to keep logic close to the entity that logic affects and not scattered around in hooks. You might be altering a form related to the node or doing with access or operate on something related to an entity with a service.

In this example, Book is not precisely a node, but it contains a node of type Book in its $entity property. All the business logic related to Book node types will be contained in this class.

final class Book implements LoanableInterface {
  private const FIELD_BOOK_TITLE = 'field_full_title';
  private $entity;

  public function label(): TranslatableMarkup {
    return $this->entity
      ->{static::FIELD_BOOK_TITLE}
      ->value ?? t('Title not available');
  }

  public function author(): Person {...}
  public function checkAvailability(): bool {...}

}

Then, in your hooks, services, and plugins, you call those methods. The result: cleaner code. 

// This uses the 'title' base field.
$title = $book->label();

// An object of type Author.
$author = $book->owner();

// This uses custom fields on the User entity type.
$author_name = $author->fullName();

//Some books have additional abilities and relationships
if ($book instanceof LoanableInterface) {
  $available = $book->checkAvailability() === LoanableInterface::AVAILABLE;
}

Business logic for books goes in the Book class. Business logic for your service goes in your service class. And on it goes.

If you are directly accessing field data in various places ($entity->field_foo->value), this is a big clue you need an entity wrapper like Typed Entity.

Focusing on entity types

Wrapping your entities does not provide organization for all of your custom code. In Drupal, however, entity types are the primary integration point for custom business logic. Intentionally organizing them will get you 80% of the way there.

Entities have a lot of responsibilities.

  • They are rendered as content on the screen
  • They are used for navigation purposes
  • They hold SEO metadata
  • They have decorative hints added to them
  • Their fields are used to group content, like in Views
  • They can be embedded

Similar solutions

This concept of keeping business logic close to the entity is not unique. There is a core patch to allow having custom classes for entity bundles.

When you call Node::load(), the method will currently return an instance of the Node class, no matter what type the node is. The patch will allow you to get a different class based on the node type. Node::load(12) will return you an instance of the Book class, for example. This is also what the Bundle Override module was doing.

There are some drawbacks to this approach.

  • It increments the API surface of entity objects. You will be able to get an instance of the Book class, but that class will still extend from the Node class. Your Book class will have all of the methods of the Node class, plus your custom methods. These methods could clash when Drupal is updated in the future. Unit testing remains challenging because it must carry over all the storage complexity of the Node class.
  • It solves the solution only partially. What about methods that apply to many books? Or different types of books, like SciFiBook or HistoryBook. An AudioBook, for example, would share many methods of Book but be composed differently.
  • It perpetuates inheritance, even into the application space. Framework logic bleeds into the application and business logic. This breaks the separation of concerns. You don’t want to own the complexity of framework logic, but this inheritance forces you to deal with it. This makes your code less maintainable. We should favor composition over inheritance.

Typed Entity’s approach

You create a plugin and associate it to an Entity Type and Bundle. These are called Typed Repositories. Repositories operate at the entity type level, so they are great for methods like findTaggedWith(). Methods that don’t belong to a specific book would go into the book repository. Bulk operations are another good example.

Typed Entity is meant to help organize your project’s custom code while improving maintainability. It also seeks to optimize the developer experience while they are working on your business logic.

To maximize these goals, some tradeoffs have been made. These tradeoffs are consequences of how Drupal works and a desire to be pragmatic. While theory can help, we want to make sure things work well when the rubber meets the road. We want to make sure it is easy to use.

Typed Entity examples

Your stakeholder comes in and gives you a new requirement: “Books located in Area 51 are considered off-limits.”

You have started using Typed Entity, and this is what your first approach looks like: 

/**
 * Implements hook_node_access().
 */
function physical_media_node_access(NodeInterface $node, $op, AccountInterface $account) {
  if ($node->getType() !== 'book') {
    return;
  }

  $book = \Drupal::service(RepositoryManager::class)->wrap($node);
  assert($book instanceof FindableInterface);
  $location = $book->getLocation();
  if ($location->getBuilding() === 'area51') {
    return AccessResult::forbidden('Nothing to see.');
  }

  return AccessResult::neutral();
}

You already have a physical_media module, so you implement an access hook. You are using the global repository manager that comes with Typed Entity to wrap the incoming $node and then call some methods on that Wrapped Entity to determine its location. 

This is a good start. But there are some improvements we can make.

We want the entity logic closer to the entity. Right now, we have logic about “book” in a hook inside physical_media.module. We want that logic inside the Book class.

This way, our access hook can check on any Wrapped Entity and not care about any internal logic. It should care about physical media and not books specifically. It certainly shouldn’t care about something as specific as an “area51” string.

  • Does this entity support access checks?
  • If so, check it.
  • If not, carry on.

Here is a more refined approach:

function physical_media_node_access(NodeInterface $node, $op, AccountInterface $account) {
  try {
    $wrapped_node = typed_entity_repository_manager()->wrap($node);
  }  
  catch (RepositoryNotFoundException $exception) {
    return AccessResult::neutral();
  }

  return $wrapped_node instanceof AccessibleInterface
    ? $wrapped_node->access($op, $account, TRUE)
    : AccessResult::neutral();
}

If there is a repository for the $node, wrap the entity. If that $wrapped_entity has an access() method, call it. Now, this hook works for all Wrapped Entities that implement the AccessibleInterface.

This refinement leads to better:

  • Code organization
  • Readability
  • Code authoring/discovery (which objects implement AccessibleInterface)
  • Class testability
  • Static analysis
  • Code reuse

How does Typed Entity work?

 So far, we’ve only shown typed_entity_repository_manager()->wrap($node). This is intentional. If you are only working on the layer of an access hook, you don’t need to know how it works. You don’t have to care about the details. This information hiding is part of what helps create maintainable code.

But you want to write better code, and to understand the concept, you want to understand how Typed Entity is built.

So how does it work under the hood?

This is a declaration of a Typed Repository for our Book entities:

/**
 * The repository for books.
 *
 * @TypedRepository(
 *    entity_type_id = "node",
 *    bundle = "book",
 *    wrappers = @ClassWithVariants(
 *      fallback = "Drupal\my_module\WrappedEntities\Book",
 *      variants = {
 *        "Drupal\my_module\WrappedEntities\SciFiBook",
 *      }
 *    ),
 *   description = @Translation("Repository that holds business logic")
 * )
 */
final class BookRepository extends TypedRepositoryBase {...}

The "wrappers" key defines which classes will wrap your Node Type. There are different types of books, so we use ClassWithVariants, which has a fallback that refers to our main Book class. The repository manager will now return the Book class or one of the variants when we pass a book node to the ::wrap() method. 

More on variants. We often attach special behavior to entities with specific data, and that can be data that we cannot include statically. It might be data entered by an editor or pulled in from an API. Variants are different types of books that need some shared business logic (contained in Book) but also need business logic unique to them.

We might fill out the variants key like this:

variants = {
  "Drupal\my_module\WrappedEntities\SciFiBook",
  "Drupal\my_module\WrappedEntities\BestsellerBook",
  "Drupal\my_module\WrappedEntities\AudioBook",
}

How does Typed Entity know which variant to use? Via an ::applies() method. Each variant must implement a specific interface that will force the class to implement ::applies(). This method gets a $context which contains the entity object, and you can check on any data or field to see if the class applies to that context. An ::applies() method returns TRUE or FALSE. 

For example, you might have a Taxonomy field for Genre, and one of the terms is “Science Fiction.” 

Implementing hooks

 We can take this organization even further. There are many entity hooks, and Typed Entity can implement these hooks and delegate the logic to interfaces. The logic remains close to the Wrapped Entity that implements the appropriate interface.

The following example uses a hypothetical hook_entity_foo().

/**
 * Implements hook_entity_foo().
 */
function typed_entity_entity_foo($entity, $data) {
  $wrapped = typed_entity_repository_manager()->wrap($entity);
  if (!$wrapped instanceof \Drupal\typed_entity\Fooable) {
    // if the entity not fooable, then we can't foo it
    return;
  }
  $wrapped->fooTheBar($data);
}

This type of implementation could be done for any entity hook.  

Is this a good idea? Yes and no. 

No, because Typed Entity doesn’t want to replace the hook system. Typed Entity wants to help you write better code that is more efficient to maintain. Reimplementing all of the hooks (thousands of them?) as interfaces doesn’t further this goal.

Yes, because you could do this for your own codebase where it makes sense, keeping it simple and contained. And yes, because Typed Entity does make an exception for hooks related to rendering entities.

Rendering entities

The most common thing we do with entities is to render them. When rendering entities, we already have variants called “view modes” that apply in specific contexts.

This is starting to sound familiar. It sounds like a different type of wrapped object could overlay this system and allow us to organize our code further. This would let us put everything related to rendering an entity type (preprocess logic, view alters, etc.) into its own wrapped object, called a renderer. We don’t have to stuff all of our rendering logic into one Wrapped Entity class.

Typed Entity currently supports three of these hooks:

  • hook_entity_view_alter()
  • hook_preprocess()
  • hook_entity_display_build_alter()

Renderers are declared in the repositories. Taking our repository example from above, we add a "renderers" key: 

/**
 * The repository for books.
 *
 * @TypedRepository(
 *    entity_type_id = "node",
 *    bundle = "book",
 *    wrappers = @ClassWithVariants(
 *      fallback = "Drupal\my_module\WrappedEntities\Book",
 *      variants = {
 *        "Drupal\my_module\WrappedEntities\SciFiBook",
 *      }
 *    ),
 *    renderers = @ClassWithVariants(
 *      fallback = "Drupal\my_module\Renderers\Base",
 *      variants = {
 *        "Drupal\my_module\Renderers\Teaser",
 *      }
 *    ),
 *   description = @Translation("Repository that holds business logic")
 * )
 */
final class BookRepository extends TypedRepositoryBase {...}

If you understand wrappers, you understand renderers.

The TypedEntityRendererBase has a default ::applies() method to check the view mode being rendered and select the proper variant. See below:

These renderers are much easier to test than individual hook implementations, as you can mock any of the dependencies.

Summary 

Typed Entity can help you make your code more testable, discoverable, maintainable, and readable. Specifically, it can help you: 

  • Encapsulate your business logic in wrappers
  • Add variants (if needed) for specialized business logic
  • Check for wrapper interfaces when implementing hooks/services
  • Use renderers instead of logic in rendering-specific hooks
  • Add variants per view mode.

All of this leads to a codebase that is easier to expand and cheaper to maintain.  

Apr 16 2021
Apr 16

Matt and Mike have Front-end core committer Lauri Eskola on to talk about the new Drupal core theme starterkit that can be used to generate new themes. We'll talk about what's been done, and what's in store for this new Drupal core feature.

Apr 02 2021
Apr 02

Mike and Matt discuss various aspects of Lullabot's Support & Maintenance Department, and how it differs from Client Services, with Director, David Burns, and Project Manager, Cathy Theys.

Mar 10 2021
Mar 10

You Might Also Like

Drupal has been around for a long time. Officially, it’s been around for at least 20 years.

Even if you jumped on the wagon around Drupal 5 or 6, that is still over 12 years of Drupal. You might have started to look longingly at the greener grasses of other ecosystems, new technology stacks, and polished products that feel fresh and new. 

Burn-out happens. Disillusionment happens. Boredom happens.

If you stare at something long enough, you start magnifying all of its flaws and forgetting some of its virtues. Familiarity breeds contempt. We get it.

But there are reasons to stick with Drupal, and there are ways to get excited about it again. You might not be able to relive the honeymoon phase back when you first installed it, but you might be able to have something better: a mature, trusted partnership.

It’s not you, it’s me - get realistic

It can be frustrating to feel like you are in a rut. Drupal is old. PHP is even older. Neither one is the new kid on the block anymore. Everyone else looks like they are having the time of their lives playing with new stuff. 

There is always something exciting happening in the Javascript ecosystem. Products like Contentful and other JAMStack solutions come along that feel fresh. Even WordPress looks like it’s having more fun.

But let’s be realistic. 

Drupal can help you solve real problems. In many cases, Drupal will help you solve them better than any other toolset on the market. There is still excitement to be found in helping solve these complicated, thorny problems.

Problems like:

While it sometimes feels like all cool kids have moved on to greener pastures, Drupal has matured and found a steady groove. It is content to sit by the fire on a Friday night with a good book and a hot cup of tea. It can look back on its youth with fondness, maybe a little embarrassment, and be confident about the future. It doesn’t need to chase after invitations to the hottest parties.

The Drupal community remains solid. It does a great job watching out for issues related to backward-compatibility, stability, and security, and all of this work is easy to take for granted. Virtually any customization can be built on its shoulders.

Yes, Drupal has changed. But haven’t we all? You might need to shift your mindset. Why did you start using Drupal in the first place? Was it to chase the newest technology? Was it to help you solve particular problems? Clarify and think deeply about your “why.” 

Even if you leave the Drupal ecosystem, one thing will remain a constant no matter where you go: yourself. Be sure the disillusionment you feel isn’t baggage you will be carrying with you.

Even if you leave the Drupal ecosystem, one thing will remain a constant no matter where you go: yourself.

Remember that newer technologies are built by people. Other communities are made up of people. No matter what toolset you are using, you will be solving problems for other people, defined by other people.

And whenever people are involved, you get people problems. Some problems are more obvious than others, but you could find yourself jumping to another ship already full of holes, taking on water.

To summarize:

  • Be realistic.
  • Know yourself.
  • Set proper expectations.

If you do these things, you might find that Drupal isn’t so bad after all. In fact, it’s still pretty great.

Double-down and dig deep - lean into craftsmanship

Come for the code; stay for the community. For many excellent reasons, that’s the motto that many people still recite and believe in. 

But what if you’re burned out on the community?

You can always stay for the code.

Drupal’s codebase indeed feels bigger and more complicated since the switch to Drupal 8, but at the same time, the code has never been more transparent. Code organization and typehints allow for better IDE (Integrated Development Environment) autocompletion. It has never been easier to discover how things work.

By making it a habit of going down rabbit holes, you can gain knowledge of Drupal’s internals, and this knowledge comes with a fulfilling sense of capacity, flexibility, and usefulness. This level of introspection allows you to build features that are as extendable as Drupal itself.

Solving problems with confidence is addicting. It may feel unintuitive to cure your disillusionment by diving deeper. It feels like the opposite of what you want to do.

But it can spark a new passion. So try doubling down. Set up your debugger and step through some code.

Start with one of the subsystems, like cache invalidation. It has a lot of moving pieces, touches different parts of the Drupal codebase, and is a big infrastructure improvement from previous versions of Drupal. Not many people have a deep knowledge of its inner workings, and knowing how all the joints fit together can enhance the solutions you build.

Or maybe you are curious about the full render pipeline. Or the CKEditor integration? Whatever it is, get curious and find some focus.

Gaining this deeper expertise and understanding can help you solve deeper problems an organization may have. You can start creating deeper solutions that have a more long-lasting impact. These can come in the form of performance improvements, better code maintainability, or better automation. You will be able to tackle more complicated problems and architect more elegant solutions.

Some other things to do to improve your craft while also directly benefiting your projects: 

  • Focus on maintainability by mapping custom code to S.O.L.I.D. principles.
  • Extract and segregate business logic into typed entities. This also means learning how to ask better questions.

Embrace the archipelago - trade and share

Since the advent of Drupal 8, we are fond of saying that Drupal finally moved off of the island. We embraced Object-oriented best practices. Symfony components now power many of the underlying subsystems that Drupal depends upon. We shed more of our “not invented here” approach.

Except it’s not entirely true that we left the island. There are still many things unique to Drupal, and to implement Drupal at scale, you need to have deep Drupal expertise. Not just Symfony or PHP expertise. Drupal expertise.

However, we did move our island closer to all of the others. Our ports participate in more shipping lanes and trade routes. We became more conscientious members of the open-source archipelago.

If you are feeling antsy to get off the Drupal island, lean into this.

Don’t just build things for Drupal. Look for opportunities to extract logic into separate libraries with full test coverage, then loop them back into Drupal. Centarro did this with currency formatting for PHP applications. Lullabot did this with an AMP PHP library that anyone can use. Thinking this way presents new challenges and introduces you to new conversations.

Drupal’s commitment to content APIs, like JSON:API, opens up more opportunities to interface with various Javascript technologies and their communities.  Building a front-end for Drupal no longer means you have to work exclusively in Drupal. React, Vue.js, and many other frameworks are waiting for you to explore.

Even without committing to a fully decoupled front-end, you can still flex these muscles in creative ways. For example, you could create a widget registry that contains reusable pieces of content. These widgets can be served on Drupal or anywhere else.

It has never been easier to island-hop while maintaining a home in Drupal. Likewise, it has never been easier to grow skillsets while working in Drupal that will serve you well if you do eventually decide to leave. 

This leads to our final point.

Focus on transferable skillsets

We have already hinted at this. When you double-down on becoming a great Drupal developer and getting familiar with the internals, focusing on object-oriented best practices, you will find yourself right at home if you start developing a Symfony application.

Likewise, the Javascript you learn to consume Drupal APIs doesn’t care where the content comes from. The skills you learn will transfer.

If you feel disillusioned with Drupal, focusing on these transferable skills can help you get excited again because they can only open more opportunities for you. You won’t have a voice in the back of your mind whispering that you are stuck. With this comes confidence, and with confidence comes excitement.

As Drupal has matured, so have the tools surrounding Drupal. Package management, automated testing, and DevOps are all things you can learn about. Not only will they help your current projects, but they each present marketable skills outside of Drupal.

Deep knowledge of Vagrant, Docker, and some of the continuous deployment platforms have an immediate payoff and will pay dividends in the future. 

Drupal doesn’t have to be your final destination. It is a useful partnership of opportunity. It can be a springboard to other things. Sometimes, recognition of this fact alone is enough to spark some excitement. 

Knowing you can leave any time you want makes it easier to stick around for a while.

Conclusion

Drupal has been around a long time, and sometimes it can be hard to maintain enthusiasm and excitement for it. But it can still help you solve difficult problems. Sometimes, you just need to shift your mindset a little.

Dig deeper and find joy in craftsmanship, try to package up libraries to trade with other communities, and recognize your ability to build a transferable skill set.

It’s hard to overcome burn-out, and you might need to step away. But if you leave, be sure you do it with clarity and intention, making sure you don’t leave anything valuable on the table.

Thanks to Mateu Aguiló Bosch, Marcos Cano, Andrew Berry, Mike Herchel, and David Burns for contributing ideas to this article

Feb 15 2021
Feb 15

You Might Also Like

In our many years of helping clients, large and small, build and optimize their websites, Lullabot has seen a common pattern in digital projects: recurring cycles ranging from energy and enthusiasm to disillusionment and destruction.

This wave pattern is common, and you can see so many instances of it. There’s the Dunning-Kruger Curve, where you swing from the peak of Mount Stupid to the Valley of Despair when you learn a new skill. There’s the Economic Life Cycle of Boom, Bust, and Recovery that our economy grinds through over and over and over. There are the relentless cycles of high tide and low tide, and of course, the actual, endless waves of the ocean. The pattern is common because that’s the way nature and humans operate.

The Digital Destruction Wave

Let’s take a hypothetical, but pretty typical, example. Acme Corporation has a popular website that’s been in operation for nearly 10 years. They have hundreds of pages of content and thousands of images and a small editorial team that is increasingly frustrated with the work it takes to create new content. They’re using an old CMS, and they’ve seen others that appear to have nicer features. Their site looks tired anyway, and they’re ready for something new.

There’s enthusiasm for the idea of starting over.  When comparing something they know intimately (with all its problems) to alternative solutions, Acme relies on demos and salespeople and marketing information that highlight the best features of the new solutions and mention little, if anything, about their limitations. 

Acme stakeholders settle on a new solution. Everyone is optimistic and very ready to get rid of the current CMS. They tackle the months-long process of designing the new solution, building it out, and populating it with all the old content, and they finally launch it.

But after launch, cracks start to emerge. Editors find that some things are harder to do than they anticipated, and some things that used to work just fine now require compromises.  Some things are easier to do, but others are harder. Editors want to be able to do “anything,” but the new implementation focused on the need for simplicity, consistency, and stability by limiting editorial options. Political realities within the organization rather than the site's actual needs drove other decisions that further complicated the solution. 

The list of requested changes and improvements grows as users and stakeholders actually use the site and realize they need more features or a different experience. Every new requirement complicates the UX and UI, adds hundreds of lines of new code, and increases the frustration. Sooner or later, the new solution is weighed down with just as many problems as the original. And the next wave of digital destruction begins as everyone wonders whether there is some other, better solution out there.

Building for the Long Term

Lullabot primarily builds Drupal sites, sometimes monolithic stacks where Drupal delivers the entire digital experience, sometimes decoupled sites with a Drupal back end and React (or something similar) on the front end.  And every year, we see this relentless wave pattern of clients reworking their digital properties in a quest for better digital experience and content management tools. Sometimes they’re coming from another CMS hoping that Drupal will finally solve their problems. Other times they have a Drupal site that has become bloated and inefficient, and they’re looking for a new solution.

Destroying everything and starting over every few years is enormously expensive and disruptive. 

But is it inevitable?

Below are three reasons why organizations get disillusioned with Drupal, each with ways to combat the problem.

No Priorities - Everything for Everyone

One of the main reasons organizations get disillusioned with Drupal is not unique to Drupal. It has to do with a lack of clearly defined goals.

Either they cannot articulate those goals in a meaningful way, or there are so many competing goals that there is a resistance to prioritizing. Prioritizing means agreeing on precise definitions. It means being locked in on specific directions and making some tradeoffs.

It means giving up on some grand panacea of building something that is everything for everyone. Many organizations do not want to give up on this vision. But that vision is never realistic. Trying to reach for that vision will mean disappointing everyone. You cannot say “yes” to everything.

In our experience, defining and prioritizing goals is the big problem to solve when implementing a new CMS. Failing to solve this problem upfront results in failure. Period. Though you might not realize the failure until more cracks have started to appear. Like playing against a Chess grandmaster when you are a novice, you lost after the first move, even though the game wasn’t technically over for another 27 turns.

And this type of failure has nothing to do with Drupal.

For example, the marketing team’s goal might be to shrink the bounce rate and increase time on the website. That is what “success” is to them. But the sales team keeps hearing complaints from their prospective customers that spending so much time on the website before speaking to a human was frustrating.

This type of mismatch is a recipe for disillusionment, no matter what technology you choose to use.

How do you avoid this problem?

Solving this problem begins long before the project kickoff. In some cases, it starts before you even know you need the help of an outside vendor.

You must have the organizational governance to be able to make decisions and enforce those decisions. All stakeholders must feel as though they are heard. There must be clear and open communication and the ability to have difficult conversations.

Sometimes an outside party, like Lullabot, can help you have these conversations. An outside voice can cut through some of the noise and tease out information from shy stakeholders hesitant to speak up. The sooner you uncover potential mismatches and risks, the better off you will be when deciding priorities.

This can feel like an extended phase one. This is foundational work that sometimes has to do with your organization’s very identity. Why do you exist? Who are you serving? What does success look like overall? What does success look like for each department? Do they conflict? Why? What is most important?

Only after you can answer these questions should you move on to additional details. Presentation modeling workshops and other requirements gathering exercises will run so much smoother, and as a result, your new CMS has a higher chance of lasting for a long time.

Drupal is a Framework, Not a Product

Drupal is flexible, and that has been touted as one of its strengths. And it is a strength. You can do a lot with Drupal that you can’t do with other enterprise content management systems. It has a wide breadth, where it can be used for things beyond landing pages, such as blogs, event systems, listing pages, contact forms, CRMs, e-commerce, and more.

But do enterprise content management systems really need that type of breadth? In the age of micro-services, a CMS product can do a few things really, really well and then integrate with other services to fill in the gaps. Most modern enterprise CMSes are polished products geared toward solving a limited set of problems.

Drupal can also integrate with other services, but if you ask what problems Drupal is designed to solve, you will get different answers depending on who you ask and what day of the week you ask them.

A Lack of Polish

Drupal has no set of polished functionality that is highly targeted to solve a limited set of problems. It is not a product. It is a framework.

Drupal’s structured content capabilities are first class, rival many enterprise systems, and are better than any other free tool you will find on the market…but this advantage will not be visible to most stakeholders. A marketing team that wants to spin out beautiful-looking landing pages cares about certain success metrics, and the elegance of the underlying content model probably isn’t one of them.

To make up for this lack of initial focus, Drupal requires significant investment if you want to use it for a big project. This is offset a little by having no licensing fees to pay (which, for some enterprise CMSs, can run into the millions of dollars per year). This type of customization and investment comes with its own risks, like missed timelines and blown budgets.

The lack of constraints and limitations also means that other products will almost always have a more polished editorial experience. With Drupal, you often have to settle with “good enough,” and, outside of some core features, polished functionality is rare. Or at least, the cost of that final 10% of polish is both time and cost-prohibitive.

This can all contribute to disillusionment, especially if expectations have not been set correctly. The grass will look greener on the other side. The spit and polish of a product demo can be alluring, especially when that demo doesn’t have to contend with your organization's underlying complexities and competing priorities.

Flexibility: Strength and Weakness

Because it is a flexible framework, Drupal is also complex. It is a set of tools that can be used in many different ways. The problem is that a screwdriver is not opinionated in how you use it. It will totally let you swing it and use it as a hammer. Sometimes, it might even work as a hammer.

Many Drupal installations end up looking like they were built by using screwdrivers as hammers. They work. Barely. But the lack of craftsmanship shows, making the system fragile and difficult to work on. Any attempts to change certain parts can cause everything to collapse.

When customized for mission-critical functionality, serving hundreds of editors across different internal teams, Drupal implementations require deep expertise. If this expertise is not present, you run the risk of building a brittle codebase that is hard to maintain and hard to extend. An extensible, flexible framework that becomes rigid and fragile is a perfect recipe for disillusionment.

How do you avoid these problems?

You need to be unrelentingly honest. You need to be clear about your goals and priorities (as discussed above), and you need to measure any polished functionality against those goals and priorities. It can be tempting to fall for the siren song of slick interfaces and smooth aesthetics.  But maintain clear eyes on what is important and impactful.

A new product may be what you need. A completely revamped editorial experience may be what you need.

But are you sure? Is the need more than skin deep?

Some other ways to ensure your Drupal implementation lasts for the long-term:

  • Have deep expertise. If you are trying to implement Drupal at scale, where it is mission-critical to your business, you need true Drupal experts. Not just PHP experts or Symfony experts. Drupal experts. If you can’t have them on your staff, you need to hire one of the top Drupal agencies (like Lullabot). You also need an architect with Drupal expertise who has the ability to see the big picture and can help be a bridge between your organization’s domain expertise and its technical implementation in Drupal.
  • Do not expect the slick editorial experience of a focused, polished product. Instead, you should manage expectations and keep the focus on what is actually important. Under the proper care, Drupal can provide you with an editorial experience that matches your business needs, but you need to reserve your budget for things that actually matter. Understand what makes for a good editorial experience, and Drupal will hold up very well when measured against that standard. Just don’t expect it to fly you to the moon. A pick-up truck doesn’t need the best stereo system in the world to do what it does best.
  • Make sure you need the flexibility of Drupal. You need to ask if Drupal is really suitable for your organization. Drupal’s strength is in its flexibility. That means, to really get gains versus other systems, Drupal needs to be used for situations that have unique needs and require extensive customization. Do you have multiple audiences seeking out content in multiple formats? Are you a large organization with various departments, each with its own editorial team, and need to maintain both consistency and flexibility?

Growth, Maintenance, and Technical Debt

This spins out of the previous section on Drupal being a flexible framework. These problems are not unique to Drupal, but because of Drupal’s unique strengths, these problems can manifest in unique ways.

Each installation has its quirks. Each implementation will make different decisions that map better to an organization’s needs.

This can lead to frustration in the future. Even if Drupal has been deployed in an expert fashion, problems will start to sprout. Organizations are not static, and the software that serves them cannot remain static.

Teams roll off, and new teams roll on. Turnover can cause gaps in knowledge. As new features are added, the potential for performance problems increases. Forms get longer and longer and more cluttered. Technical debt attaches to projects like barnacles on a ship. Drupal, and the many dependencies it relies on, must be updated and maintained.

Since Drupal isn’t opinionated, similar functionality can be implemented in many different ways. Different teams have different styles, and these styles can start to clash if there is no proper continuity plan. This leads to less code reuse and a breakdown in the overall organization.

All of these things can start to cascade like a slow-moving avalanche, eventually ending the honeymoon period of a successful launch and leaving a wide swath of disillusionment in its wake. 

How do you avoid these problems?

  • Maintain continuity after team roll-offs. This can come in many forms. Good documentation is always encouraged, though that requires effort on its own, and it requires the next team to care about it. It is better to focus on people. You can have at least one developer overlap both teams, and they work to continue the same standards. Or you can have the same company pick up support and maintenance for the project. The concept of “office hours” can work. These are set times when members of the new team work with some members of the old team who helped launch the project.
  • Be explicit when mapping business logic. Settling on a ubiquitous language and modeling that language with classes in your code can limit technical debt and make future maintenance smoother. Using something like Typed Entities to help keep your custom business logic separate can make it easier for new team members to jump in. These build fences around how developers extend Drupal, which leads to more explicit expectations and reigns in some of the disadvantages of Drupal’s radical flexibility.
  • Maintain a clear view of your priorities. Managing your priorities doesn’t stop after the project launch. This takes us back to the very beginning. Your organizational governance must hold the line. New features must be filtered. New priorities and requirements must be surfaced in an organized manner. Unregulated growth is not good for any organism, and it remains true for your CMS implementation.

Conclusion: Noble Retirement

Your CMS investment can last a long time. And now, with Drupal’s new major releases not requiring migrations, your investment should last a long time. You don’t need to be subject to the waves of disillusionment and destruction.

Your investment won’t last forever. But it should last long enough for you to retire it on your own terms, long after you have seen an ROI. Send it off into the sunset with full honors, knowing that it did its duty and served you well.

Early destruction and re-creation are not inevitable. Manage your priorities, set reasonable expectations, ensure you have deep expertise at your disposal, and maintain continuity and consistency. These are easier said than done, but they can be done. Instead of being beaten down by the heavy waves of destruction, you can surf them with grace.

Contributed to by Karen Stevenson, Greg Dunlap, Mike Herchel, Mateu Aguiló Bosch, Chris Albrecht, Nate Lampton, Sean Lange, Marcos Cano, Monica Flores

Nov 05 2020
Nov 05

Matt and Mike talk with Sascha Eggenberger about the Gin admin theme, including its editorial interface changes, relationship to the Claro theme, future and more!

Gin admin theme showing the node edit page
Oct 07 2020
Oct 07

About host Matt Kleve

Portrait of Matt Kleve

Matt Kleve has been a Drupal developer since 2007. His previous work in the media sparks a desire to create lean, easy to use workflow processes.

About host Mike Herchel

Thumbnail

A senior front-end developer, Mike is also a lead of the Drupal 9 core "Olivero" theme initiative, organizer for Florida DrupalCamp, maintainer for the Drupal Quicklink module, and an expert hammocker

Jul 15 2020
Jul 15

Continuous Deployment, Infrastructure as Code, and Drupal

The previous article of this series provided an overview of setting up a GitHub Actions workflow that would publish changes into a Kubernetes cluster. This article takes you through each of the steps of such a workflow.

For reference, here is the GitHub Actions workflow in the sample repository at .github/workflows/ci.yml:

on:
  push:
    branches:
      - master
name: Build and deploy
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/[email protected]
        with:
          fetch-depth: 1

      - name: Build, push, and verify image
        run: |
          echo ${{ secrets.PACKAGES_TOKEN }} | docker login docker.pkg.github.com -u juampynr --password-stdin
          docker build --tag docker.pkg.github.com/juampynr/drupal8-do/app:${GITHUB_SHA} .
          docker push docker.pkg.github.com/juampynr/drupal8-do/app:${GITHUB_SHA}
          docker pull docker.pkg.github.com/juampynr/drupal8-do/app:${GITHUB_SHA}
      - name: Install doctl
        uses: digitalocean/[email protected]
        with:
          token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}

      - name: Save cluster configuration
        run: doctl kubernetes cluster kubeconfig save drupster

      - name: Deploy to DigitalOcean
        run: |
          sed -i 's||docker.pkg.github.com/juampynr/drupal8-do/app:'${GITHUB_SHA}'|' $GITHUB_WORKSPACE/definitions/drupal-deployment.yaml
          kubectl apply -k definitions
          kubectl rollout status deployment/drupal
      - name: Update database
        run: |
          POD_NAME=$(kubectl get pods -l tier=frontend -o=jsonpath='{.items[0].metadata.name}')
          kubectl exec $POD_NAME -c drupal -- vendor/bin/robo project:files-configure
          kubectl exec $POD_NAME -c drupal -- vendor/bin/robo project:database-update

There are several systems involved in the above workflow. Here is a diagram that illustrates what is happening:

Building an image

The first step at the GitHub Actions workflow builds a Docker image containing the operating system and the application code, along with its dependencies and libraries. Here is the GitHub Actions step:

 - name: Build, push, and verify image
  run: |
    echo ${{ secrets.PACKAGES_TOKEN }} | docker login docker.pkg.github.com -u juampynr --password-stdin
    docker build --tag docker.pkg.github.com/juampynr/drupal8-do/app:${GITHUB_SHA} .
    docker push docker.pkg.github.com/juampynr/drupal8-do/app:${GITHUB_SHA}
    docker pull docker.pkg.github.com/juampynr/drupal8-do/app:${GITHUB_SHA}

The Docker images are named with the Git commit hash via the environment variable {GITHUB_SHA}. This way, you can match deployments with commits in case you need to review or roll back a failed deployment. We also created a personal access token to authenticate against GitHub Packages and saved it as a GitHub Secret as PACKAGES_TOKEN.

The Dockerfile that docker build uses for building the Docker image is quite simple:

FROM juampynr/drupal8ci:latest
COPY . /var/www/html/
RUN robo project:build

The base image is juampynr/drupal8ci. This image is an extension of the official Drupal Docker image, which extends from the official PHP Docker image with an Apache web server built in. It has a few additions like Composer and Robo. In a real project, you would use Ubuntu or Alpine as your base image and define all the libraries that your application needs.

The last command at the above Dockerfile, robo project:build, is a Robo task. Robo is a PHP task runner used by Drush among others. Here is the output of running this task in a GitHub Actions run:

Step 3/3 : RUN robo project:build
199 ---> Running in 2b550db4d25e
200 [Filesystem\FilesystemStack] _copy [".github/config/settings.local.php","web/sites/default/settings.local.php",true]
201 [Composer\Validate] Validating composer.json: /usr/local/bin/composer validate --no-check-publish
202 [Composer\Validate] Running /usr/local/bin/composer validate --no-check-publish
203./composer.json is valid
204 [Composer\Validate] Done in 0.225s
205 [Composer\Install] Installing Packages: /usr/local/bin/composer install --optimize-autoloader --no-interaction
206 [Composer\Install] Running /usr/local/bin/composer install --optimize-autoloader --no-interaction
207Loading composer repositories with package information
208Installing dependencies (including require-dev) from lock file

In essence, robo project:build copies a configuration file and downloads dependencies via Composer.

The result of the docker build  command is a Docker image that needs to be published somewhere the Kubernetes cluster can pull it during deployments. In this case, we are using GitHub Packages to host Docker images. 

Connecting GitHub Actions with the Kubernetes cluster

At this point, we have built a Docker image with our application containing the latest changes. To deploy such an image to our Kubernetes cluster, we need to authenticate against it. DigitalOcean has a Kubernetes action that makes this easy via doctl, its command-line interface.

- name: Install doctl
  uses: digitalocean/[email protected]
  with:
    token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}

- name: Save cluster configuration
  run: doctl kubernetes cluster kubeconfig save drupster

The first step uses DigitalOcean’s GitHub action to install doctl while the second one downloads and saves the cluster configuration, named drupster.

As for the DigitalOcean setup, there is nothing fancy about it. You can just sign up for a Kubernetes cluster which uses three nodes (aka Droplets in DigitalOcean’s lingo):

The Kubernetes Dashboard link at the top right corner takes you to a page that shows an overview of the cluster:

 The above dashboard is a great place to monitor deployments and look for errors. Speaking of deployments, this is what the next section covers.

Deploying the image into the cluster

Here is the step that deploys the Docker image to DigitalOcean:

- name: Deploy to DigitalOcean
  run: |
    sed -i 's||docker.pkg.github.com/juampynr/drupal8-do/app:'${GITHUB_SHA}'|' $GITHUB_WORKSPACE/definitions/drupal-deployment.yaml
    kubectl apply -k definitions
    kubectl rollout status deployment/drupal

The first line fills a placeholder with the image being deployed, which came from the commit hash. The next two lines perform the deployment and verify its status.

Note: We will inspect each of the Kubernetes objects in the next article of this series, but if you are curious, you can find them here.

After the deployment is triggered, the Kubernetes master node will destroy the pods and create new ones containing the new configuration and code. This process is called rollout, and it may take a variable time depending on the scale of the deployment. In this case, it took around 20 seconds.

If something goes wrong while pods are being recreated, there is the chance to undo the changes by rolling back to a previous deployment configuration via kubectl rollout undo deployment/drupal.

Updating the database

If the deployment succeeded, then the next step is to import configuration changes and run database updates. An additional command was added to ensure that file permissions are correct due to issues found while setting up the workflow). Here is the step:

- name: Update database
  run: |
    kubectl exec deployment/drupal -- vendor/bin/robo project:files-configure
    kubectl exec deployment/drupal -- vendor/bin/robo project:database-update

The above step completes the workflow. 

Next in this series…

The next and last article in this series will cover each of the Kubernetes objects in detail.

Acknowledgments

If you have any tips, feedback, or want to share anything about this topic, please post it as a comment here or via social networks. Thanks in advance!

Jun 18 2020
Jun 18

Drupal 7 to 9 Upgrade

If you're one of the 70% of Drupal sites that are still on Drupal 7 at the time of this writing, you may be wondering what the upgrade path looks like to go from Drupal 7 to Drupal 9. What does the major lift look like to jump ahead two Drupal versions? How is this different than if you'd upgraded to Drupal 8 sometime in the last few years? And how long will it be before you have to do it again?

Upgrading via Drupal 8

Before the release of Drupal 9, the best path for Drupal 7 sites to upgrade to Drupal 9 was to upgrade to Drupal 8. The big selling point in Drupal 9's evolution is that updating from a late version of Drupal 8 to Drupal 9.0 is more like an incremental upgrade than the massive replatforming effort that the older Drupal migrations used to entail. Sites that jumped on the Drupal 8 bandwagon before Drupal 9.0 was released could benefit from the simple upgrade path from Drupal 8 to Drupal 9.0 instead of another big migration project.

Migrating to Drupal 8 is still a good option for Drupal 7 sites, even though Drupal 9 is now out.

You might find that the essential modules or themes you need are ready for Drupal 8 but not yet available for Drupal 9. The Drupal 8 to Drupal 9 upgrade path for many modules and themes should be relatively trivial, so many of them should be ready soon. But, there could be some outliers that will take more time. In the meantime, you can do the heavy lift of the Drupal 7 to Drupal 8 migration now, and the simpler Drupal 8 to Drupal 9 upgrade later, when everything you need is ready.

The Drupal 7 to Drupal 8 migration

The Drupal 7 to Drupal 8 upgrade involves some pretty significant changes. Some of the things you previously needed to do via contributed modules in Drupal 7 are now included in Drupal 8 core. However, the way you implement them may not be the same as some refactoring might be required to get feature parity when you migrate to Drupal 8. 

The migration itself isn't a straight database upgrade like it was in Drupal 6 to Drupal 7; instead, you can migrate your site configuration and site content to Drupal 8. You have a choice of doing it two ways: 

  1. Migrate everything, including content and configuration, into an empty Drupal 8 installation (the default method).
  2. Manually build a new Drupal 8 site, setting the content types and fields up as you want them, and then migrate your Drupal 7 content into it. 

For a deeper dive into what these migrations look like, check out An Overview for Migrating Drupal Sites to 8

Planning migrations

The Migration Planner is a helpful tool you may want to consider in your migration planning process. This tool queries a database to generate an Excel file that project managers or technical architects can use to help plan migrations. Developers who are performing the migrations can then use the spreadsheets.

Performing migrations

Core comes with some capability to migrate content automatically. If your site sticks to core and common contributed content types and fields, you may be able to use these automatic migrations. However, if your site relies heavily on contributed modules or custom code, an automatic migration might not be possible; you may need a custom migration approach.

The Drupal Migrate UpgradeMigrate Plus, and Migrate Tools modules are good starting points for performing a custom migration. They add things like Drush support for the migration tasks and migration support for some non-core field types. You can access several custom migration processors that make it easy to do some fairly complex migrations. This can be done just by adding a couple of lines to a YAML file, like an entity_lookup processor that will take text from Drupal 7 content and do a lookup to determine what Drupal 8 entity the text refers to.

Drupal 7 works on older versions of PHP but recommends a minimum of 7.2. If you're migrating from an older Drupal 7 site, there may be several other platform requirements to investigate and implement. 

Tooling and paradigm shifts

With the change to Drupal 8, developers are also expected to use new tools. You now use Composer to add modules and their dependencies, rather than Drush. Twig has replaced PHPTemplate as the default templating engine. Some core paradigms have shifted; for instance, developers need to learn to think in terms of events, or extending objects, instead of the old system of hooks. Many hooks still work, but they will probably be deprecated over time, and the new methods are safer ways to write code. The changes aren't insurmountable, but your organization must invest in learning the new way of doing things. You'll need to account for this education overhead when coming from Drupal 7; development teams may need more time to complete tasks as they learn new tools and paradigms.

Drupal 8's deprecation model

In addition to big changes in Drupal 8 core and implementation details, Drupal 8 also features a deprecation model that's familiar in the software world, but new in Drupal version upgrades. Instead of deprecating a bunch of code when there's a major version upgrade, Drupal 8 has introduced a gradual deprecation model. 

As features and improvements are made in Drupal 8's codebase, old methods and functions are marked as deprecated within the code. Then, a few versions later - or in Drupal 9 - that code is removed. This gives development teams a grace period of backward compatibility, during which they can see alerts that code is deprecated, giving organizations time to implement the new code before it's completely removed. 

The deprecated code also provides an easy hint about how to rework your code using new services and methods. Just look at what the hook does, and do that directly in your code.

This gradual deprecation model is one of the core reasons that the Drupal 9 upgrade is more like a minor version release for Drupal 8 than a major replatforming effort.

Jumping straight to Drupal 9

With that said, can you jump ahead from Drupal 7 to Drupal 9? If you want to skip over Drupal 8 entirely, you can jump directly to Drupal 9. The Drupal 7 migration ecosystem is still available in Drupal 9. Drupal 9 contains the same migrate_drupal module you need to migrate to Drupal 8. There has been discussion around possibly moving this module to a contributed module by Drupal 10, although no decision has been made at the time of this writing.

If you intend to go this route, keep in mind that all of the considerations when upgrading from Drupal 7 to Drupal 8 apply if you jump straight to Drupal 9, as well. You'll still have to manage the migration planning, deal with tooling and paradigm shifts, and consider platform requirements.

Ultimately, however, jumping directly from Drupal 7 to Drupal 9 is a valid option for sites that haven't migrated to Drupal 8 now that Drupal 9 is released. 

When to migrate to Drupal 9

Whichever route you choose, whether you're going to migrate via Drupal 8 or straight to Drupal 9, you should start the migration from Drupal 7 to Drupal 9 as soon as possible. Both Drupal 7 and Drupal 8 will reach end-of-life in November 2021, so you've got less than a year and a half to plan and execute a major platform migration before you'll face security implications related to the end of official Drupal security support. We'll cover that in more detail later in this series. 

For any site that's upgrading from Drupal 7, you'll need to do some information architecture work to prepare for the migration to Drupal 8 or Drupal 9. Once you're on Drupal 8, though, the lift to upgrade to Drupal 9 is minimal; you'll need to look at code deprecations, but there isn't a major content migration to worry about. Check out our Preparing for Drupal 9 guide for more details around what that planning process might look like.

But what about waiting for a later, more stable version of Drupal 9, you ask? This is a common strategy in the software world, but it doesn't apply to the Drupal 9 upgrade. Because Drupal 9 is being handled more like an incremental point-release upgrade to Drupal 8, there aren't any big surprises or massive swaths of new code in Drupal 9. The core code that powers Drupal 9 is already out in the world in Drupal 8. There are no new features in the Drupal 9.0 release; just the removal of code that has already been deprecated in minor versions of Drupal 8.

Going forward, the plan for Drupal 9 is to release new features every six months in minor releases. The intent is for these features to be backward compatible, and to bring Drupal into the current era of iterative development versus the major replatforming projects of olde. There aren't any big surprises or major reliability fixes on the horizon for Drupal 9; just continued iteration on a solid platform. So there's no need or benefit to waiting for a later version of Drupal 9!

How to migrate to Drupal 9

Plan for migration

Planning for a Drupal 7 to Drupal 8 or Drupal 7 to Drupal 9 migration becomes a question of scope. Do you just want to migrate your existing site's content into a modern, secure platform? Or are you prepared to make a bigger investment to update your site by looking at information architecture, features, and design? Three factors that will likely shape this decision-making process include:

  • Time and budget
  • Developer skillset
  • Release window

Time and budget for a migration

How much time are you able to allocate for what is likely to be a major replatforming effort? What's your budget for the project? Do you need to launch before an important date for your organization, such as college registration or an important government deadline? Can your budget support additional work, such as a design refresh? 

For many organizations, getting the budget for a large project is easier as a one-time ask, so doing the design refresh as part of the migration project may be easier than migrating, and then planning a separate design project in six months. In other organizations, it may be difficult to get enough budget for all the work in one project, so it may be necessary to spread the project across multiple phases; one phase for the migration, and a separate phase for design.

When factoring in the time and budget for additional work, keep in mind that things like revisiting a site's information architecture could save you time and money in the migration process. Budgeting the time to do the work up-front can dramatically save in time and cost later in the migration process, by reducing unnecessary complexity before migrating instead of having to work with custom migrations to bring over content and entity types that you don't use anymore. This also improves maintainability and saves time for developers and editors doing everyday work on the new site.

Consider developer skills when planning your migration

10 years is a long time for developers to be working with a specific framework. If you've been on Drupal 7 since 2011, your developers are likely very experienced with "the Drupal 7 way" of doing things. Many of those things change in Drupal 8. This is a big factor in developer resistance around upgrading to Drupal 8 and Drupal 9.

Composer, for example, is a huge change for the better when it comes to managing dependencies. However, developers who don't know how to use it will have to learn it. Another big difference is that a lot of Drupal 8 and Drupal 9's core code is built on top of Symfony, which has changed many mental paradigms that experienced Drupal developers are accustomed to using. While some things may seem unchanged - a Block is still a Block, for example - the way they're implemented is different. Some things don't look the same anymore; developers will encounter things like needing to use YAML files instead of hooks to create menu items. Even debugging has changed; things like simple debugging via print() statement doesn't always cut it in the new world, so many developers are using IDEs like PHPStorm, or a host of plugins with other editors, just to code effectively in newer versions of Drupal.

All of this change comes with overhead. Developers must learn new tools and new ways of doing things when switching from Drupal 7 to Drupal 9. That learning curve must be factored into time and budget not only for the migration itself but for ongoing development work and maintenance after the upgrade. Progress during sprints will likely slow, and developers may initially feel resistant or frustrated while they learn the new ways of doing things.

Bringing in outside help during the migration process can mitigate some of this learning overhead. Partnering with an experienced Drupal development firm means your migration can be planned and implemented more quickly. One thing to consider when selecting an outside partner is how closely they collaborate with your internal team. When choosing a Drupal development firm to collaborate with your internal team, consider the value of partnering with experienced developers who can "teach" your internal teams how to do things. This reduces the learning curve for a team that's heavily experienced with older Drupal versions and can help your team get up to speed more quickly - saving money during the first year of your new site.

Plan a release window

The other aspect of planning for the Drupal 7 to Drupal 9 upgrade is planning a release window. Plan to have your migration project complete before Drupal 7 is scheduled to reach end-of-life in November 2021. If you can't make that deadline, then start planning now for an Extended Support engagement to keep your site secure until you're able to complete the migration.

You'll want to plan the release window around key dates for your organization, and around other support windows in your stack. For example, if you're a retailer, you may want to have the migration completed before the end of Q3 so you're not upgrading during holiday initiatives. Education organizations may plan their release during slow periods in the school's calendar, or government websites may need to be ready for key legislation. 

When it comes to your stack, you'll want to plan around other important release windows, such as end-of-support for PHP versions, or upgrading to Symfony 4.4. This is particularly important if you need to upgrade dependencies to support your Drupal 7 to Drupal 9 migration. Check out Drupal 8 Release Planning in the Enterprise for more insights about release planning.

Revisit information architecture, features, and design

Because the jump from Drupal 7 to Drupal 9 is so substantial, this is a good time to revisit the information architecture of the site, do a feature audit, and consider whether you want to make design changes. 

Is it time to update your site's information architecture?

Before you jump into a Drupal 9 upgrade project, you should perform an audit of your existing Drupal 7 site to see what you want to carry forward and what you can lose along the way. Did you set up a content type that you only used once or twice, and never touched again? Maybe you can delete that instead of migrating it. Are you using a taxonomy that was set up years ago, but no longer makes sense? Now is a good time to refine that for the new version of your site.

Content migration is also a relatively easy time to manipulate your data. You can migrate Drupal 7 nodes or files into Drupal 9 media entities, for instance. Or migrate text fields into address fields or list fields into taxonomy terms. Or merge multiple Drupal 7 content types into a single Drupal 9 content type. Or migrate content from a deprecated Drupal 7 field type into a different, but supported, Drupal 9 field type. These kinds of things take a bit more work in the migration, but are completely possible with the Migration toolset, and are not difficult for developers with migration experience. The internet is full of articles about how to do these kinds of things.

In addition to the fine details, it's also a good time to take a look at some big-picture questions, like who is the site serving? How has this changed since the Drupal 7 version of the site was established, and should you make changes to the information architecture to better serve today's audience in the upcoming Drupal 9 site? 

Have your feature needs changed?

Drupal 7 was released in 2011. Nearly a decade later, in 2020, the features that seemed important at Drupal 7's inception have changed. How have the feature needs of your content editors changed? Has your site become media-heavy, and do your content editors need large searchable image archives? Do you want to deliver a dynamic front-end experience via a bespoke React app, while giving content editors a decoupled Drupal framework to work in? 

Many editors love the new Layout Builder experience for creating customized site pages. It's something that doesn't exist in Drupal 7 core and is arguably better than what you get even when you extend Drupal 7 with contributed modules. Drupal 8 and 9 have built-in media handling and a WYSIWYG editor, eliminating the need for dozens of Drupal 7 contributed modules that do not always cooperate with each other, and focusing developer attention on the editorial UX for a single canonical solution.

Revisit the needs of your content editors and site users to determine whether any existing features of the current site are no longer important and whether new feature needs warrant attention in the upgrade process. This could be particularly helpful if you find that current features being provided by contributed modules are no longer needed; then you don't have to worry about whether a version of those modules is available in Drupal 8/9, and can deprecate those modules.

Ready for a design update?

If your Drupal 7 site hasn't had a design refresh in years, the upgrade process is a good time for a design refresh. Plan for a design refresh after the upgrade is complete. Drupal 9 will have a new default theme, Olivero, which features a modern, focused design that is flexible and conforms with WCAG AA accessibility guidelines. Olivero has not yet been added to Drupal core - it's targeted to be added in 9.1 - but it is available now as a contributed module any Drupal 8 or Drupal 9 site can use. Olivero is a great starting point for sites that want an updated design. 

If you're planning a custom design project, keep accessibility and simplicity at the forefront of your design process. You may want to engage in the design discovery process with a design firm before you plan your Drupal 9 release; a good design partner may make recommendations that affect how you proceed with your migration.

Perform the migration

The process of migrating from Drupal 7 to Drupal 8 has improved since Drupal 8's initial release, but it can still be an intricate and time-consuming process for complex sites. We wrote An Overview for Migrating Drupal Sites to 8 to provide some insight around this process, but upgrading sites must:

  • Plan the migration
  • Generate or hand-write migration files
  • Set up a Drupal 8 site to actually run migrations
  • Run the migrations
  • Confirm migration success
  • Do some migration cleanup, if applicable

Unlike prior Drupal upgrades, migrating to Drupal 8 isn't an automatic upgrade. A Drupal 7 site's configuration and content are migrated separately into a new Drupal 8 site. There are tools available to automate the creation of migration files, but if you've got a complex site that uses a lot of custom code or many contributed modules, you'll only go so far with automated tools. You'll need to revisit business logic and select new options to achieve similar results or deprecate the use of Drupal 7 contributed modules and custom code in your site to move forward to Drupal 8 and Drupal 9.

Whether you're going to upgrade to Drupal 8 and then Drupal 9, or migrating directly from Drupal 7 to Drupal 9, these migration considerations and the process itself will be the same. The only difference would be whether the new site you migrate content into is a Drupal 8 site or a Drupal 9 site.

Upgrading from Drupal 8 to Drupal 9

If you choose to go through Drupal 8, once you get to Drupal 8, finishing the migration to Drupal 9 is relatively easy. Upgrade to the latest version of Drupal 8; the upgrade to Drupal 9 requires Drupal 8.8.x or 8.9.x. Along the way, you'll be notified of any deprecated code or contributed modules you'll need to remove before upgrading to Drupal 9. Make sure any custom code is compatible with Drupal 9, and then update the core codebase to Drupal 9 and run update.php

Voila! The long upgrade process is complete. 

About Drupal Sun

Drupal Sun is an Evolving Web project. It allows you to:

  • Do full-text search on all the articles in Drupal Planet (thanks to Apache Solr)
  • Facet based on tags, author, or feed
  • Flip through articles quickly (with j/k or arrow keys) to find what you're interested in
  • View the entire article text inline, or in the context of the site where it was created

See the blog post at Evolving Web

Evolving Web