ContinuousIntegration

From APIDesign

(Difference between revisions)
Jump to: navigation, search
(U Can't Test This!)
(Control the Observables)
Line 9: Line 9:
== Control the Observables ==
== Control the Observables ==
-
As such one has to do something else. Rather than testing all the depending code, one has to make sure the externally observable aspects of your framework as stable - e.g. they don't share like the [[amoeba]] in the [[Amoeba Model]]. What does that mean?
+
As such one has to substitute the continuous execution of every piece of code by something else. Rather than testing all the depending code, one has to make sure the [[API|externally observable aspects]] of your framework are stable - e.g. they don't shake like an [[amoeba]] in the [[Amoeba Model]]. What does that mean?
-
# Don't rename exported symbols
+
# Don't rename externally visible symbols
# Don't change method signatures
# Don't change method signatures
# Don't change the exposed behavior
# Don't change the exposed behavior
-
Item #1 is important for ''linkage'' - e. g. to make sure that various modules ([[JAR]]s or '''.so''' or '''.js''' files) built on top of your [[API]] find the symbol they expect. Proper linkage is a per-requisite for any other communication. If the users of your [[API]] cannot connect to it, the rest is completely useless.
+
Item #1 is important for ''linkage'' - e. g. to make sure that various modules ([[JAR]]s or '''.so''' or '''.js''' files) built on top of your [[API]] find the symbol they expect. Proper linkage is a prerequisite for any other communication. If the users of your [[API]] cannot connect to it, the rest is completely useless.
-
Keeping unchanged method signatures is important in compiled languages. This is often important for linkage as well. In case of [[Java]] one can use [[Sigtest]] to prevent accidental changes of number or types of parameters. In dynamic languages like [[JavaScript]] this check has to be done dynamically and thus it shifts the check closer to the [[Runtime_Aspects_of_APIs]].
+
Keeping stable method signatures is important in compiled languages. This is often part of the linkage as well. In case of [[Java]] one can use [[Sigtest]] to prevent accidental changes of number or types of parameters. In dynamic languages like [[JavaScript]] this check has to be done dynamically and thus it shifts the check closer to the [[Runtime_Aspects_of_APIs]].
-
It is easy to say to keep existing behavior, but what if it buggy? Well, sometimes even buggy (from one perspective) behavior can be useful (see the [[Arithmetica]] example). What can you do when you really need to change it? In such case support [[AlternativeBehavior]]s! There are few [[AlternativeBehavior|ways to do it]] applicable to any [[language]] (for example [[XML]] does that by encoding its own version in the document header), but the best is when the language or underlaying [[NetBeans Runtime Container|runtime container]] provides some support to it.
+
As far as runtime compatibility goes, it is easy to say: ''keep existing behavior''. But what if such behavior is buggy? Well, sometimes even buggy (from one perspective) behavior can be useful (see the [[Arithmetica]] example) and should be preserved. What can you do when you really need to change it?
 +
 
 +
In such case choose to provide [[AlternativeBehavior]]s! There are few [[AlternativeBehavior|ways to do it]] applicable to any [[language]], but the best is when the language underlaying [[NetBeans Runtime Container|runtime container]] provides some support to it.
 +
 
 +
For example [[XML]] does that by encoding its own version in the document header and than it is clear which parser to use to provide the right [[AlternativeBehavior]]. On the other hand [[Python]] is famous for not identifying its [[language]] version and changing its syntax in a slight incompatible way that started years of the ''2.x'' vs. ''3.0'' battle.
== What's the Goal? ==
== What's the Goal? ==

Revision as of 15:20, 3 April 2018

There is nothing wrong on having your own ContinuousIntegration server like Jenkins or Hudson set up. Having it contributes to quality of your project. As Chapter 9 of TheAPIBook explains, tests are the sole of good frameworks and the most important part of good projects.

Contents

U Can't Test This!

People get so used to this comfortable safety net provided by ContinuousIntegration systems that they tend to forget there is also something behind the horizon. On the other hand the quality departments are well aware of that (regardless of the amount of CodeCoverage) there can always be errors visible to end users.

The situation is even more complicated when it comes to API Design. A common advice to get better quality is to increase your CodeCoverage or add integration tests to verify the real workflow. Yet the biggest problem when designing an API of a framework is: you can't run the users code! Either you don't have it, or you don't even know about it. When your framework gets really successful, then running all projects that use your API wouldn't even scale.

Control the Observables

As such one has to substitute the continuous execution of every piece of code by something else. Rather than testing all the depending code, one has to make sure the externally observable aspects of your framework are stable - e.g. they don't shake like an amoeba in the Amoeba Model. What does that mean?

  1. Don't rename externally visible symbols
  2. Don't change method signatures
  3. Don't change the exposed behavior

Item #1 is important for linkage - e. g. to make sure that various modules (JARs or .so or .js files) built on top of your API find the symbol they expect. Proper linkage is a prerequisite for any other communication. If the users of your API cannot connect to it, the rest is completely useless.

Keeping stable method signatures is important in compiled languages. This is often part of the linkage as well. In case of Java one can use Sigtest to prevent accidental changes of number or types of parameters. In dynamic languages like JavaScript this check has to be done dynamically and thus it shifts the check closer to the Runtime_Aspects_of_APIs.

As far as runtime compatibility goes, it is easy to say: keep existing behavior. But what if such behavior is buggy? Well, sometimes even buggy (from one perspective) behavior can be useful (see the Arithmetica example) and should be preserved. What can you do when you really need to change it?

In such case choose to provide AlternativeBehaviors! There are few ways to do it applicable to any language, but the best is when the language underlaying runtime container provides some support to it.

For example XML does that by encoding its own version in the document header and than it is clear which parser to use to provide the right AlternativeBehavior. On the other hand Python is famous for not identifying its language version and changing its syntax in a slight incompatible way that started years of the 2.x vs. 3.0 battle.

What's the Goal?

The goal is to make sure that people can write applications, but more importantly libraries and plugins that work with multiple versions of your API. Nobody wants to publish update as soon as you provide new version of the API. Once we can achieve that we can say that your API is keeping BackwardCompatibility.

NetBeans IDE supported plugins since 2000 and the most important feature people asked for what ability to write a plugin that would work with last few versions of NetBeans simultaneously. Their core business wasn't to write plugins, so they wanted to minimize the time spend doing that. The last thing they wanted was to watch us to release new version of NetBeans and at that time publish an update of their plugin suitable to run in that version.

The same situation applies to transitive dependencies between libraries. If somebody builds their API on top of yours then there has to be a configuration for these two APIs to work together. By doing non-BackwardCompatible changes one complicates that a lot (remember LibraryReExportIsNPComplete) which can be justified by everyone who tried to assemble five pieces into a single working system in an environment which pays little attention to BackwardCompatibility.

Conclusion

Having ContinuousIntegration is a must for any project (including ones that provide an API). However in contrast to building final applications (thus let's call it another paradox of APIDesign) one cannot fit the whole code into the ContinuousIntegration check. As such one needs to apply also other techniques when judging correctness of a change. Keeping BackwardCompatibility is a useful initial step that always helps.

Personal tools
buy