|Status:||DRAFT, under review (initial content is subjective interpretation of list discussions by author, for review and contributions by others)|
|Intended Audience:||Apache Flex FlexJS SDK/Compiler developers (a separate document could be created for users of FlexJS SDK releases, based on relevant content)|
Review time frame:
PAYG - "Pay as you Go"
Pay as you go (PAYG) is a general principle for FlexJS whereby the SDK framework code (the actionscript codebase used to create FlexJS SDK library swcs) supports incremental functionality in components. The goal is to provide the possibility for a lower level of performance or memory impact of applications built using the framework (compared to not applying PAYG to the framework code). PAYG may imply a small performance or memory cost in the framework to support its implementation, but that is recouped in its use. Some PAYG aspects may also be provided by or supported by compiler options which may not have any corresponding framework overhead.
To understand PAYG, it's very helpful to understand some background. The opposite of PAYG is "Just in Case" code. Classic Flex architecture took this "Just in Case" approach and it resulted in a bloated framework. Just in Case relies very heavily on class inheritance and has a lot of code that you might need for certain components. A perfect example of this is UIComponent which is close to 15,000 lines of code with hundreds of methods that are often not needed at all. Being that UIComponent is the base class for every UI component in Flex, every application is carrying around this code whether it needs it or not. Additionally, much of this code is being run at runtime whether or not it's needed for the specific use case. This is wasteful in terms of disk space, network resources, memory and runtime performance overhead.
What is not PAYG?
todo: list any possible common misconceptions (or remove this topic)
What is 'Paying' in PAYG
'Paying' is defined as conscious decisions by developers to include functionality that has a measurable and substantial performance or memory impact. The ‘as you go’ implies ‘incremental functionality’. In simple terms, it means that you do not include performance and memory ‘cost’ for functionality you don’t use. -Harbs I'm not sure this is the best way to put it. Below is my attempt to clarify this. (not sure I did the best job with the "clarifying" part...)
The word 'Paying' can be replaced by the word 'Code'. Pay as You Go is synonymous with 'Code as You Go'. That is (in general), any code which is not essential for the functioning of the basic component should be composed to be used "as you go" elsewhere. This elsewhere can be a subset, an alternate set, a composable bead etc. The point is that "just in case" code should not be included in the basic minimal useful implementation. It should be included only when you want it, or opt in to "just in case" code.
The ‘paying’ aspect of PAYG does not include 'developer effort' as ‘payment’ or ‘cost’. This means that for some projects using a full PAYG component set, additional developer effort will be required, however the FlexJS project team is working to reduce this by composing sets of general purpose components that include ‘general purpose’ functionality (See Doesn’t supporting PAYG have its own cost? below).
How is ‘measurable and substantial’ defined to determine thresholds for default functionality?
Determining whether something has merit for inclusion in default functionality or is categorised into ‘as you go’ is something that has generated discussions in the flex lists and has resulted in disparate views. Perhaps part of the reason for this is a lack of clarity and/or consensus as to whether ‘paying’ includes developer effort or not (see What is ‘Paying’ above).
How do we decide what is default functionality and what is ‘as you go’?
What does ‘substantial’ mean? And in what context?
Are there exceptions or inconsistencies that are justifiable (and under what conditions)?
Doesn’t supporting PAYG have its own cost?
Performance cuts both ways. PAYG may contribute to performance costs, because there is some overhead in supporting a runtime based composition model. On the other hand, "Just in Case" code is not being run, so that more than likely offsets the overhead. Moreover, the general principle is that establishing the ability to compose functionality (especially via mxml) is worth the overhead. For features that are opt-in via the compiler, these features may not have an extra framework cost.
Because there can be extra effort to compose incremental functionality for common use cases, it would likely result in extra developer effort being required if the developer was using a full PAYG approach to development on every project. For this reason, the ‘Express’ component set exists which has pre-composed components that feature combinations of functionality to suit more general use cases. The intention behind this is to reduce the time and effort required in the use of common components by including the more commonly needed functionality. The vision is that there should be a collection of component sets which compose the functionality required for specific types of applications. This can help balance ease of development against PAYG benefits.
PAYG vs DRY
Another software design concept is DRY (Don't Repeat Yourself). PAYG can sometimes be at odds with DRY. Considering that additional functionality would need to be in a different class, this can lead to code repeat and multiple classes which have a lot of the same logic.
Despite this, we feel that the benefits of PAYG out way the costs of sometimes failing to adhere to DRY.
However, we don't want to ignore DRY and it's worth noting that it's not always an issue, and the cost can be mitigated. Many of these cases are simply alternate implementations of the same type of component. When picking the component set, the minimal usable implementation should be selected. If the larger implementation is needed, and is used consistently across the application, this code duplication might never make it into the end application. Additionally, use of subclasses in extended components can result in less code duplication.
An additional technique which is used frequently in framework code is the use of utility classes or top level functions (note: top level functions should be used more than it currently is) to implement commonly used code that is needed across different components. This utility code will only be included if it's actually used, and only be included once no matter how many times it's used. Utility code should be small and serve a single purpose.
How do we decide if a piece of functionality should be default/baseline functionality or ‘extra’/optional functionality?
todo: this needs to provide an actual guide for decision making. Currently it has questions that might help to arrive at such a guide
Suggested goal for this topic area: a decision framework with weighting on areas of importance when considering various factors.
The aim is to remove any lack of clarity and reduce any future debate around specific topics to a discussion around relevance to documented (consensus-based) criteria.
Current status: There is no definitive guide, suggested discussion topics
Discussion areas (What factors could be considered for this decision process, and what is their relative weighting in decisions).
1. What is ‘minimum’ vs. pareto-based functionality?
-Is the effort of avoiding functionality worth more than the cost of including it (is there a practical value in avoiding it - will any developer actually prefer to use the 'minimum' or is it to remain 'true' to PAYG principles, for example) .
-There appears to be general agreement that the Flex 4 sdk had ‘too much’ swiss-army-knife code, attempting to cover too much functionality in base classes and therefore had extra un-needed ‘weight’ or baggage in most applications. This has been often used as the justification for a PAYG approach, what is the right balance of functionality for the Express package.
Are there downsides to aiming too much in the opposite direction, or is it always justified? For FlexJS, what is the 20% of functionality/features that address 80% of needs (assuming pareto principles apply – 80/20 is only indicative for discussion here, not literal values). Is this always ‘express’ vs. ‘baseline’ or not? If baseline is not in line with pareto principles, what percentage of developers would find it useful to work with directly (or is it mainly intended for support of ‘express’ and other modules)?
2. How much ‘weight’ does an optional piece of functionality add? Is there a way to specify a threshold for what is considered baseline/default and what (for example) would be included in express component set?
3. Do additional compilation steps automatically eliminate un-used functionality, and how does that relate to PAYG?
-If yes, this could favour inclusion of greater functionality by default, because the ‘payment’ for unused functionality is essentially zero if it is optimised away by (for example, via Google Closure Compiler) when it is not used. Counter: what disadvantages are there with relying on an external process to do this?
4. Are all targets considered equal?
5. What other factors might be important in the future?
The sands keep shifting, the idea here is to draw a line in the sand, at a point that anticipates the shape of the dunes in the relevant future. We have done this before.
As an example, here is a guiding statement that was documented 4 years ago which was true then, but has been addressed in ES6 (WeakMap) and is now supported by current browsers (but notably, not IE 9 -10, for example, which are still considered current FlexJS targets).
“Also, since Dictionary and weak references don't really exist on HTML/JS, the new framework will not use them on AS and you may have to explicitly call release() or some other API on some objects to make them available for garbage collection.“
How does PAYG get implemented?
todo: Discuss and populate
1. Strands and Beads
The ‘strands and beads’ concept is a metaphor for adding extra functionality (beads) onto a component (strand). Beads represent the modular units of functionality that can be selected and composed together to configure functionality added to the base component (strand). Component sets such as ‘Express’ include more general functionality which may into reduce developer effort for the general case of application development.
Where do beads with additional functionality go? What determines inclusion in the sdk and which module (e.g. would some beads only be part of Express or are they central and Express is only a set of components composed of commonly used beads)? What criteria would we potentially have to suggest them being excluded from FlexJS (i.e. ‘that belongs more in an external library’ if that is ever a need).
General discussions to derive guidance:
todo: discussion When to include, when to exclude
Other ‘safety’ aspects
-Automatic removal of listeners/general ‘cleanup’ when removing functionality
Initial content: This should be done (in a bead) when a bead is intended to be removable, which is unlikely for the most basic implementation of that 'type' of bead. Therefore it should not be in the base class if the incremental functionality for that bead is inheritance based.
Approaches to Incremental functionality via beads
todo: discussion (resolve varied views)
Is there one preferred approach to coding beads (or?) ?
-New (isolated) Beads (copy paste with extra functionality) vs. DRY
-Bead inheritance and/or utility functions
-Beads of Beads?
1. Compiler options.
initialised vs. uninitiliased variables
This is a topic of current discussion, with differing views.
todo: discussion (resolve varied views)