Everybody loves stuff that just work 🤟🏽. But getting there is a long journey!

Discussing with some people at the Nextcloud Conference in Berlin last week, a recurrent pain is app compatibility with each new server version. Nextcloud releases new versions every 4 months. Then, each individual app's maintainer needs to test if they work with the new version, fix any possible breakage, and manually self-report as compatible.

A common reason for an app to break is that it uses APIs that are not marked as stable. So I tried to help the discussion by bringing some data with the ANEBA prototype 🦠.

The ANEBA search portal

You can access it here: aneba.sitegui.dev

With the declared goal of the project to have more apps available, pains like this will likely only grow if left unattended.

A quick word about Nextcloud

Nextcloud is an open source cloud platform. It can host files, photos, office documents, notes, lists, calendars, chats, you name it! It's made of a core server, some pre-installed apps (like Files) and other community-driven apps.

I use it for almost two years for me and my family, replacing my previous use of Google Drive, Photos, Calendar. I self-host it at home in an old laptop, but the software can impressively scale down to a Raspberry Pi and up to multiple federated servers. For example, in Technische Universität Berlin:

Nextcloud in Technische Universität Berlin

PHP and contracts

Beware that I haven't written a Nextcloud app myself, I only have second-hand knowledge about the matter. So my explanations here are probably correct and flawed at the same time 🫣

It's interesting to analyse some of the technological aspects behind the scenes: Nextcloud server is written in PHP and provides a rich way for apps to hook into it. There are two major ways that apps can be implemented: "classic" apps and "external" apps (ExApp). For ExApps, their backend is an independent process or container, and it talks to the server using an HTTP API.

But this post is mostly about classic apps, which are the majority. A classic app provides new PHP source files that implement server logic, declare new database tables, and bundle UI files for its pages. For each request routed to the app, its code runs in the same process as the Nextcloud server, using PHP's autoloader to import and run the necessary files. The Nextcloud server has a public PHP API available at the \OCP namespace (side note: OC means Own Cloud, the project from which Nextcloud forked in 2016).

However, by the nature of the PHP language, apps can also import and use server internals at \OC (or OC_App if you're vintage). Some of this use is justifiable when there is no equivalent public API available for what the app needs. However, some uses should no longer be. Maybe the code is old, or was copied from an old example, or AI generated, and in the meantime the necessary functionality was added to \OCP.

Talking to the team responsible for the server and these APIs, they are accutely aware of these problems. The good path forward is to implement new \OCP APIs that answer to the apps' needs and keep these new APIs stable for longer.

The first prototype

If we simplify the problem, we could try to answer: which apps are using \OC and where? A simple regex could help, but I wanted to have more precision and insights.

Luckily, there is a PHP parser written in Rust (which seems to be linked to a pretty cool project for a Language Server Protocol for PHP) 🦀

So the steps to draw the owl are:

  1. download all apps
  2. parse all their PHP files
  3. resolve all referenced symbols
  4. detect all uses of \OC
  5. do something with it

The first version worked, but we wanted to know more! For example, we would also like to detect the use of items in \OCP that are marked as deprecated. We would like to know which apps use another app's internals.

So during the contributor week, I wrote ANEBA for this.

To show the maginitude of the computational problem:

Server version 35 34 33
Compatible apps 311 477 517
Archive volume 2.2 GiB 4.1 GiB 4.4 GiB
PHP source files 132 k 176 k 187 k
Analysis time 7 s 9 s 11 s

Rust is fast 🤓

Making it useful

The results of the scan are an unexplored gold mine for both the server team and app developers! So to help them use this data, I've created a search portal at aneba.sitegui.dev. Data is only useful if trustworthy and actionable.

There, the server team can search which private APIs are most dependent upon to propose public and more stable alternatives. They can also search which private APIs are no longer necessary and can be dropped. They can anticipate breakage on future releases and alert maintainers.

The app developers can see the scan result of their apps and where possible, replace with alternatives.

If you find any problem with the results or a missing use-case, send me an email.

Next steps and dreams

Displaying the data in a search portal is just a first step. We would like to integrate these analyses directly where developers are:

  • for app developers, we could add a lint rule to point those usages and make this lint part of the suggested workflow in the template app repo
  • for server developers, we could detect if a given PR breaks too many apps and pushes a message to the PR with this information
  • for the Nextcloud administrators, we could give transparent information of apps that depend on internal APIs, to alert that these apps may take longer to update to newer server versions

ANEBA uses static analysis to parse the declaration and use of PHP types (classes, interfaces, etc). But currently, it does not do any type analysis. So to achieve these greater goals, it has to get smarter.

To illustrate the limits of the current approach, consider this simple code $a->b(). To know which exact method "b" it's using, we need to resolve the type of $a. That's a larger can of dancing worms.