Using Static Analysis & Custom Rules to Support Refactors

My first draft of this post followed on from the story I started to tell in A New Day. I want to try to keep the story of the factory under wraps for a little while longer but I will share that I'll be speaking about it quite soon! If you happen to be in the area, do come on by.
Today is more focused on a particular business concern - understanding user behaviour - or at least the beginning of that journey.
Suppose that you have built a mobile app with many screens, buttons, textfields, you name it. You release your project to the world and it's successful beyond your wildest dreams and you have over one million users! There are many comments where people describe their experiences, some are really positive (e.g. "amazing!! worked perfectly"), some are really negative (e.g. "this was a complete waste of time"), almost all of them are quite vague in terms of specifically what the user is hoping to get out of their sessions and what they were doing to try and achieve that. Are they using it how you intended? How can you know?
We can start by collecting data about how users interact with the app - for instance, which screens they've visited and which buttons they've tapped. Suppose we have a big app with a lot of buttons, to accomplish our mission of understanding user behaviour we'll have to find all of them and add measurements. That's not necessarily trivial for large projects - an explanation of why would go into what code is, which is beyond the scope of this piece but if you stay with me for the factory tour, it is something that I will talk about.
For now let's assume we have a Flutter project that uses the various buttons provided by the Material Design System, and only these buttons. The task at hand is to take all of these buttons and modify them to do the work that they were doing before (e.g. to respond to tap events in a prescribed way) in addition to some new work, such as recording impressions or recording tap events. What we are interested in then is to create a layer of abstraction around the existing buttons. However, we will have an ongoing challenge: there is little to stop developers from adding new buttons the old way (i.e. with no analytics).
To help solve the general problem of introducing new rules to old projects, we can use Dart's static analysis tools and custom lints. Below are code excerpts that can be used to apply a custom lint package to your project that includes a single rule, banning Material Scaffold widgets from use in a project. We can use similar rules to discourage the use of any particular widget to support migrations of our code base to serve some business outcome by automatically identifying violations and providing advice to developers. I encourage everyone who works with Flutter to give it a try!
Until next time,
Mark
analyzer:
plugins:
- custom_lint
app/analysis_options.yaml
dev_dependencies:
...
custom_lint: 0.8.1
my_custom_lints:
path: ../my_custom_lints
app/pubspec.yaml
dependencies:
...
custom_lint_builder: ^0.8.1
analyzer: ^8.1.1
my_custom_lints/pubspec.yaml
import 'package:analyzer/error/error.dart' hide LintCode;
import 'package:analyzer/error/listener.dart';
import 'package:custom_lint_builder/custom_lint_builder.dart';
PluginBase createPlugin() => _Linter();
class _Linter extends PluginBase {
@override
List<LintRule> getLintRules(CustomLintConfigs configs) => [
BanInstanceOfRule('Scaffold'),
];
}
class BanInstanceOfRule extends DartLintRule {
BanInstanceOfRule(this.bannedTypeName) : super(code: LintCode(
name: 'ban_use_of_$bannedTypeName',
problemMessage: 'Adding instances of $bannedTypeName is not permitted.',
// ignore: deprecated_member_use
errorSeverity: ErrorSeverity.ERROR,
));
final String bannedTypeName;
@override
void run(
CustomLintResolver resolver,
DiagnosticReporter reporter,
CustomLintContext context,
) {
context.registry.addInstanceCreationExpression((node) {
if (node.staticType?.element?.displayName == bannedTypeName) {
reporter.atNode(node, code);
}
});
}
}
my_custom_lints/lib/my_custom_lints.dart