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: | annual |
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.
Philosophy
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.
PAYG takes the opposite approach. Classes only contain the code necessary for the minimum useful implementation to function. Instead of heavy reliance on inheritance, PAYG prefers composition and dependency injection. Instead of additional features being baked in, they are composed and functionality injected when needed. The theory is that this results in smaller, leaner applications. Results seem to back up this theory. One application which was ported from Flex4 to FlexJS was reduced from a total size of 1.8MB down to less than 500KB of Javascript gzipped. There is still potential in the FlexJS compiler to reduce the size even further. The Flash version of this application seems to be even smaller, but exact numbers are hard to determine because not all features are implemented in Flash.
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).
...
Are there exceptions or inconsistencies that are justifiable (and other under what conditions)?
Why PAYG?
To target javascript, the FlexJS SDK and components needed to be substantially different to the legacy Flex 4 SDK which is SWF-only (no need for details why here, but maybe link to other content?). Given this need to make large changes, the opportunity was also taken to review general improvements to the SDK component architecture to address some common criticisms of the Flex 4 component set. One of those criticisms (the size of compiled applications) relates to the size of some of the Flex 4 base classes which, in simple terms, is the opposite of PAYG. Implementing PAYG in FlexJS has the goal of maintaining the possibility of a full range of functionality, but making it more incremental/opt-in.
Doesn’t supporting PAYG have its own cost?
Performance
Yes, it Performance cuts both ways. PAYG may contribute to performance costs, because there is some overhead in supporting a runtime based composition model. However. 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.
Developer Effort
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
...
-Does what we decide now make sense for possible future compiler targets (i.e. other than swf or javascript) (note: there are no established plans for other targets, but the possibility of considering them might factor in, although probably/perhaps this is a low-weight factor).
How does PAYG get implemented?
todo: Discuss and populate
...
General discussions to derive guidance:
Null Checks
todo: discussion When to include, when to exclude
...
Approaches to Incremental functionality via beads
todo: discussion (resolve varied views)
Is there one preferred approach to coding beads (or?) ?
...
Compiler
1. Compiler options.
initialised vs. uninitiliased variables
This is a topic of current discussion, with differing views.
todo: discussion (resolve varied views)
Question:
is it better to be compliant with actionscript language expectations as a priority (e.g. myUninitialisedObjectVar === null vs. myUninitialisedObjectVar === undefined in javascript) or is this a case where we forgo compliance with actionscript expectations in favour of minimal code (e.g. force developer to use .myUninitialisedObjectVar == null for compatibility between targets)
Partial answer: we can support initialisation to defaults as specified by actionscript, and create a compiler option to switch it on or off. So the question then becomes : which is the default? Is full PAYG more important than compliance with actionscript (in the javascript output)? For the optional approach, what is the capacity for problems arising from having it in one state for the framework code vs. a different state for the sdk user's code?