Page MenuHomePhabricator

Identify prevalent component antipatterns in MobileFrontend
Closed, ResolvedPublic

Description

NOTE: this task is a draft and should be considered work in progress until resolution or this notice is removed.

The purpose of this task is to assess and aggregate common difficulties we have encountered when building and maintaining components in MobileFrontend.

This task is not for solutioning.

Acceptance criteria

  • The final draft is published on wiki.

Draft

The following component design patterns have been recurring and worked poorly in MobileFrontend:

  • Mixed responsibilities. Considerable boilerplate is necessary to build and compose components. This has led numerous modules to own too many concerns which diminishes readability and limits composability, reusability, and testability.
  • Do-it-all constructors. Construction of a component that assumes dependencies instead of asking for them hinders composability and testability. For Views, the superclass of all components in MobileFrontend, construction of a subclass implies template rendering, full DOM inflation, and other set up.
  • Lengthy inheritance hierarchies. Long class chains are difficult to reason about, compose, and test. Even small changes often require thorough knowledge of all ancestors and descendants, and the problem is exacerbated by JavaScript's untyped nature and the sprawling MediaWiki ecosystem.
  • All components are heavyweights. View is the superclass for every component in MobileFrontend. This single level of inheritance implies complicated lifecycles, jQuery, event buses, a class hierarchy that includes at least 500 lines of code, and other historical baggage that would be unnecessary for many use cases but is currently the responsibility of every component. Functions compose but these fully inflated View objects are far more complicated to assemble.
  • Imperative programming. MobileFrontend relies heavily on jQuery for the creation of components. Its usage has encouraged building features in a way that is both verbose and challenging to reason about, and discourages composition. In some cases, the results of our best devs have been nearly incoherent. Additionally, the outputs of the API often return more API-specific types that imply further usage such that any interaction compounds.
  • Manual DOM reconciliation. Frequently, JavaScript is used to progressively enhance a webpage originally modeled and rendered on the server. The client JavaScript must manually parse and manipulate the server's data then update the DOM with the new state. Not only is this code very tricky to write correctly, confusing to read, and costly to maintain and test, it's exceptionally fragile: changes to the server, the client, or dependent APIs each have the potential to cause unbreak now bugs.
  • No standard component patterns. There are many inconsistencies in how components are structured and composed. Many of the patterns that do exist in MobileFrontend are equally considered antipatterns. Building new features should be commonplace and harbor few surprises but instead often feel like the first time and cost like it too because components haven't found a natural expression. MobileFrontend has no paragon for composable components.
  • Informal application state management. MobileFrontend's state management is ad libbed and a rich source of bugs. DOM state, singletons, other global states, several event buses, promises, and callbacks are all used to communicate and derive the current state.
  • Implicit dependencies. Many files in MobileFrontend rely on implicit dependencies during execution. These frequently add side-effects to imports / requires which breaks tree-shaking, makes subjects difficult to test, inhibits prototyping, and impedes external code reuse by third-parties.
  • Haphazard route management. Endpoints are declared impromptu in different places such that it would be difficult to list all of the endpoints provided by MobileFrontend. This has created problems in managing browser history and UI state, providing a stable API to users, and developer conceptualization.

We think these problems are rooted in or molded by MobileFrontend's component framework. This framework is unique to MobileFrontend and has larger overarching technical, social, and product concerns:

  • Monolithic and integrated. The component framework parts of MobileFrontend are not distinct from the rest of the codebase. For example, it is not possible to "npm install" MobileFrontend's custom framework and reuse it another application. Because it is nonredistributable, this limits usage of the framework to MobileFrontend. Further, the framework is challenging to analyze or compare separately to other solutions because it's embedded in the application.
  • Informal API and versioning. Although the lack of any external users for a framework improves its malleability, the informality afforded hides significant changes that would normally be well-planned, scoped, and semantically versioned. I.e., changes regardless of their significance are often made organically with decisioning made implicitly by the needs of the moment, and as a spot fix, rather than explicitly architected and as a codebase-wide revision. For instance, an API breaking change could be made with the same gravitas and consideration as fixing a typo. Additionally, the lack of a formal and well-defined public API hinders readability, clouds responsibilities, and obscures the goals of the framework.
  • Single-purpose and non-reusable. MobileFrontend's framework is built to solve the problems at hand. It is not generic. This means that not only is it likely too specific to be used for anything but exactly what it was built for (and therefore only useful to MobileFrontend), but also that many use cases have likely been unconsidered. The framework may require an architectural overhaul unexpectedly at any time during normal feature development, which is a serious liability to product roadmaps.
  • Terra incognita. Unique tooling produces unique problems hitherto unseen. When functionality breaks or a new feature is needed, it's a first. There's no prior art, similar references have to be viewed through a distorted lens, and a brand new approach is required for this one-of-a-kind circumstance. The premise for many changes in MobileFrontend is uncharted territory which is extremely inefficient.
  • Limited vision. There is a singular user of MobileFrontend's framework: Readers Web. No one cares about even fundamental issues because no one else is using it. Issues are quite likely "unknown unknowns" in one of our blind spots. Whereas, when a well-known open-source framework makes a mistake, there are reports (even complaints), and discussions until it is resolved ("given enough eyeballs, all bugs are shallow"). MobileFrontend's homemade framework does not have a culture of development, it's growth is limited, and it's doomed to be understood and used by few and forever bounded by the abilities of a few authors.
  • Limited expertise. There is little documentation, demonstration of idiomatic patterns, or domain experts except what Readers Web itself cultivates for MobileFrontend's tailor-made framework. It is a high cost to build and maintain these from within and the learning curve for new hires is steep and daunting even when the materials exist and are up to date. When an author leaves, they take a portion of this expertise with them. When the majority of a team departs, as is the case for Readers Web, a library of knowledge is lost. Because MobileFrontend's framework is nonredistributable, there are no professional teachers, books, videos, or a universe of open-source examples to rely on.
  • Effectively closed source software. For many reasons, a nearly unsurpassable barrier to participation exists in MobileFrontend's custom framework to those outside its authorship. This approach to development is non-inclusive, does not welcome a diverse community of contributors—not only in terms of numbers, but in terms of background—and discourages the usual benefits and freedoms associated with free and open-source software. The MobileFrontend codebase, which contains the framework for one of the world's most popular websites, has only been starred by 43 people and forked 16 times in the past eight years, which is orders of magnitude less than standard FOSS alternatives. By the de facto metrics of success in the open-source community, MobileFrontend's framework has not lived up to the mission or Wikimedia engineering architecture principles. In other words, MobileFrontend's framework limits our community to a small closed set of mostly employees instead of exploring the globally diverse population our movement strives to reach and connect with. We believe products should be built by everyone for everyone so that no group's needs are excluded. Practically speaking, it is becoming increasingly harder to find contributors, even paid contributors, that can make changes to MobileFrontend, and it will only get harder as MobileFrontend's framework lags further behind.
  • Noncompetitive. MobileFrontend's anonymous framework, due to its unpopularity, isolation, limited resourcing, and closed source-like nature, cannot compete with open-source alternatives. It has never been and will never be competitive. Readers Web does not have the resources to build a competitive framework or even a framework to fulfill its current needs—nor should it strive to.
  • Built and maintained in-house. Because outside contributions are blocked by design, Readers Web is solely responsible for all development and cannot ride the usual open-source wave of global contributions, absorbing free fixes and features from the community of developers. Evolution of MobileFrontend's framework is limited to and by internal developers and their experience, creating perpetually immature and underdeveloped code that limits what user-facing features can be built. This is expensive and insufficient.
  • No escape hatch. Migration guides are only written for projects that are popular and usable elsewhere. MobileFrontend's framework is neither because it's single-purposed and nonredistributable. Readers Web will sooner or later have to unravel all code written using the existing framework, and how that code transitions to a new framework. This problem will worsen as more code is built using the custom framework.
  • Custom tooling is incompatible with other open-source software. Many tools are built for popular open-source frameworks, often with out-of-the-box support for integration. MobileFrontend's bespoke framework by default loses access to this entire ecosystem of tooling and any additional tooling needed must is roll-your-own or do without.
  • Application code is obfuscated. Frameworks are the underpinnings of applications. MobileFrontend's custom framework adds at least one layer of complexity to everything built on top of it. In effect, it silos development to Readers Web almost exclusively.
  • A poor investment. Although it is difficult to quantify, when developers perceive their technology is unsustainable, there is little appetite for mastering it, contributions wane, and the future is uncertain. To potential new contributors unfamiliar with MobileFrontend, simply spotting nonstandard or deprecated tooling may mark the entire codebase as being so far out of fashion, it's out of time; so foreign, it's alien; and to be dismissed as unmaintained. These impressions can create a spirit of demoralization.

Event Timeline

Thanks for writing this down. I agree with all of these points. While developing https://www.mediawiki.org/wiki/Extension:ExternalGuidance, I had the opportunity to extend and use it. The code was not that difficult to understand and extend to me, but I faced issues when APIs were changing. I got confused with the trade offs when there are more than one way to do the same.

The "Limited expertise", "Non competitive", "Effectively closed source", "A poor investment" and other points, I think, are applicable to our front library OOUI and to certain extend VE as well. If we are going to discuss these, consider the intersection with these projects as well. Thanks.

Thank you, @santhosh! Your perspective is very informative!

We discussed this ticket in the Frontend Standards meeting today (/cc @Mooeypoo). This task only lists problems encountered in MobileFrontend components but the sentiment from Volker, Eric, Ed, and Jon was that this problem is actually much broader to frontend development and any solution to these issues would probably be wanted elsewhere, so we should be mindful of that.

We agreed that a useful step forward would be to first identify similar (and different) component issues in other projects individuals are familiar with (e.g., ExternalGuidance, WikibaseMediaInfo, VisualEditor, etc) in new tasks that link to this ticket. There may be issues unique to certain projects but the feeling was, if I've interpreted correctly, that we have a lot of common ground. These tasks don't need to be exhaustive but adequate detail (and context) supplied by project experts are appreciated so that we can at least understand 1) shared issues and needs 2) unique or rare issues and 3) team impact and priority. (It's fine to quote problems from this task too if that's useful.) As tasks, we'll be able to more easily communicate, prioritize, plan, track, and resolve the wider needs.

It's also perfectly fine if some projects don't have any issues with components. MobileFrontend has many, as listed above, and these need to be resolved. I hope that whatever broad solution to the problems we identify across projects does not block work in Readers Web so timeline is something else I'd like to keep in mind as we move forward.

The final draft is published on wiki.

This AC is accounted for by FAWG's work in T241180 and related documents.