Skip to content

Principles of Keyman Code Changes

Eberhard Beilharz edited this page Nov 27, 2020 · 7 revisions

Introduction

These are some general principles that we need to be following when we create features or bug fixes in Keyman. These principles help to uphold a good user experience for Keyman end users and keyboard/model developers.

This is a first draft, with plenty of scope for discussion! We haven't always been consistent in following these principles and that's something I'd like to improve on.

We'll track the discussion in this google doc and update this wiki when things stabilise.

Principles of Keyman Code Changes

1. What the keyboard designer creates, we present, unmodified.

For example, we don't make changes to the keyboard key caps, nor do we insert an extra key into a popup menu. We process the rules according to the documented specification and we don't deviate from that.

2. Engine backward compatibility: A compiled keyboard or model, compiled in an earlier version MUST work unmodified in a later version.

Remember that we don't control all keyboards and models. There are many, many keyboards not in our repository, and there will also be models that are not in our repository.

Generally, I won't approve breaking changes in interfaces for models and keyboards: a 13.0 model MUST continue to work with 14.0, 15.0 and into the future. Keyman 6.0 keyboards from the 90's work with Keyman 14.0. In theory, Keyman 4.0 keyboards from 1996 would work even though I don't know if any still exist (and wouldn't be Unicode so would definitely be 'legacy').

One reason for this is that keyboards and models are distributed to users, and users may not be able to acquire a new version of the package from the package developer, nor downgrade to an earlier version of Keyman to continue using their package. Thus, an upgrade of Keyman must not break existing packages.

We cannot assume an evergreen software world for Keyman, where everything is always updated.

3. Compiler backward compatibility: A new version of the compiler MUST be able to compile a keyboard, model or package that targets an earlier version of Keyman.

The Keyman keyboard language store(&version) version targeting statement is designed to help backward compatibility. It allows developers to target a specific version of the Keyman Engine and avoid using new functionality that won't work on that version. It directs the compiler to produce a keyboard/package that will work in that earlier version.

4. Source backward compatibility: Keyboard, model and package source file formats SHOULD be backward compatible.

That is, changes to the compiler SHOULD not require keyboard or model developers to make changes to existing keyboards simply in order for them to compile with a new version.

It's okay to add compiler deprecation warnings for code that is no longer recommended. These warnings SHOULD not be shown where a keyboard/model has an older version target.

It may be okay, well down the line, to have a compiler error, for example where newer replacement code works better. This error SHOULD not be shown where a keyboard/model includes an older version target statement.

An example of this is language tagging. This is now done in packages, rather than in keyboards. However, the existing language tag statements (store(&ethnologuecode) for example) continue to work.

5. We try not to make language assumptions.

Casing rules, normalization, and other similar one-size-fits-all patterns are helpful but where we implement them, we MUST provide a pathway for a keyboard/model developer to override them.

6. Engine APIs SHOULD be forward compatible.

An API consumer that was built for Keyman 13 should continue working with Keyman 14. This is particularly applicable for desktop APIs where the version of Keyman is determined by the end user rather than the API consumer. On mobile devices, the APIs are used only by a Keyman Engine library consumer, and so there is a pathway for deprecations and changes.

Changes to APIs SHOULD be marked with deprecations for at least one major version.

An exception to this principle may be made for security requirements.

Discussion

Now you can probably see some potential for conflict between these principles. We'll fix bugs and that includes some design bugs. But even when fixing bugs we need to make sure that we don't violate the backward compatibility principles.

These may be illustrated with real case studies.

U+25CC (#3039)

We started adding a dotted circle to unattached diacritics to try and fix an issue on Android where unattached diacritics would display off-key. This however violates Principle 1 (and 5).

In Keyman 14, we are winding this back, which in theory is a backward compatibility violation (principle 3). The keyboard may not display as intended on older versions of Keyman, where a keyboard designer has assumed the presence of the U+25CC. However, this conflict can be resolved by the keyboard designer if it is important, by including a custom OSK font which guarantees the display of the characters in the right way regardless of the version.

Showing the base key in the popup menu (#3718)

This design decision violates Principle 1. Somewhat less obviously, this ended up breaking Principle 5 too.

It came into being way back when we first developed Keyman for iPhone. The idea was that we would match the default pattern of showing the base key in the popup menu that the default device keyboard did on some devices, but did not do on other devices, e.g. iPads.

Principle 5: the order of letters in the popup menu matters, for example for Amharic, where the base letter should be shown but not in first place!

So we'll remove this functionality in Keyman 14; this slight change in functionality does not break keyboards, although there will be a slight change in user experience. But in doing so, we give control back to the keyboard developer.

So following Principle 1 helps us to uphold Principle 5.

Centralising word breaking functionality (#3706)

Here, we realised we didn't need to implement the full word breaking algorithm in every model, but could centralise the code. However, in so doing, we would end up breaking custom models.

This is a design improvement. But it violates principles 2, 3 AND 4:

2: existing custom models 'compiled' with Keyman 13 would not work in Keyman 14. 3: a custom model compiled with Keyman 14 may not work in the same way in Keyman 13. Would trie models compiled with Keyman 14 work with Keyman 13? This is unclear to me. 4: custom model designers would need to make changes to their model in order to conform to the new interface, just to get it to compile with Keyman 14.

Support for und-fonipa (#xxxx)

Keyman 14 will allow us to support keyboards with und-fonipa BCP 47 tag. However, Keyman 13 on Windows did not allow us to do this. How do we support this transition? This is an open question for me.

Clone this wiki locally