PRESENTED BY Patterns That Pay Off Design, behavior, and organizational patterns that will make a real impact on your company and codebase Your Friend Matt @stauffermatt
pattern 1. a regular and intelligible form or sequence discernible in certain actions or situations 2. a model or design used as a guide in needlework and other crafts. noun
Do this on every app no matter what; e.g. “floss every day,” “avoid N+1 queries,” “avoid env() calls in non-config code” Preventative patterns Preventative vs. Reactive
Feel/see the pain then choose the fix; e.g. “root canal to treat cracked tooth,” “microservice to isolate slow adapter infrastructure,” “factory to normalize multiple exchangeable implementations” Reactive patterns Preventative vs. Reactive
$ • When you try to predict the future, you: • Commit to unrealistic timelines, leading to unhappy clients and unhappy devs • Over-architect to try to reduce Cost of Change • Become rigid, and can’t flex with new requirements • Instead, feel free to do the best work you can with what’s in front of you No precogs: the payoff
$ • Easier onboarding and less cognitive load when entering a new codebase • Good patterns become ingrained easily into company culture and new-dev onboarding • Devs spend brainpower on stuff that matters Codebase Consistency: the payoff
$ • Easier onboarding and less cognitive load when entering a new codebase • Less time wasted on “it works for me” or “did you remember to _____” • More likelihood developers will discover the steps for onboarding (devs don’t read docs ) Bin scripts: the payoff
• No code can get merged to production without two sets of eyes on it • One developer + one code reviewer • (OR) Two pair programmers Four Eyes * If any team members have more or less than two eyes, this number can be modified
$ • Avoid the negative consequences of hyper focus • “Reviewing brain” is often different from “writing brain” • Any two developers bring different perspective, broadening the team’s capabilities • Share load of responsibility for all new code introduced Four Eyes: the payoff
• Self-documenting code # • … some code (e.g. complex database queries) just takes time to grok what it does • … more code takes time to grok why it does what it does • Avoid // Show the resource • Aim for // Exclude the connections that match both user ID *and* service ID Document the weird
$ • Faster comprehension of complex code • Less wasted lines/visual debt on useless documentation • Less useless docs = people read docs more • Records business priorities Document the weird: the payoff
• Agile Manifesto != The Agile Industrial Complex • “Agile” is often waterfall, rebranded • Return to agility • “Gee, Spider-Man super is agile!” instead of “I’m a Certified Agile Scrum-Master 3000” • Stay light, respond quickly, adjust often • Militia vs. large army Lower-case agile
$ • Agile promises • React quickly to new information/stimuli • Deliver functional iterations early and often • Flexibility around different workers & different projects • Without Agile costs • No one saying “I can’t pull that because it will mess up the burndown chart” • Focus on delivering great software, not fitting a system Lower-case agile: the payoff
• You’ve heard of API-first: design the API, consume it with both JavaScript and mobile • You’ve heard of microservices: split your app into a million pieces so each is independent • Consider monolith first • API-first and microservices have their place • …but aren’t free • …especially on small teams Monolith first
$ • Avoid API-first/microservice-first costs: • More complicated and duplicated validation • More complicated and error-prone synchronization of testing data and expectations • More complicated branching and deploy strategies • Higher up-front development costs • Reduced flexibility Monolith first: the payoff
• Don’t rely on dumps of production data for new devs or testing • Build robust, randomized seeders • Check features under development on randomized seeds, not the narrower set of data-we-currently-have- in-production • Keep production data in production • Pro tip: reference production data to help you improve seeds Seeds, not dumps
$ • Easier CI/CD • Easier testing • Easier onboarding new users • Easier bug fixing • Less scared to make changes locally • Reduces fragility (less likely to only work if the data is a certain way) Seeds, not dumps: the payoff
• The best place to start your tests is by asking yourself: “what part of this app, if broken, would make me worried for my job?” • …“what’s most likely to break” • …“what do I have the least control over” • …“what are we about to refactor” • …“what would make my clients stress out” • …“what would make me stress out” Test to save your job
$ • You get to keep your job • Testing because it’s providing immediate value, not because someone told you you should • Most important tests first • You get to keep your job Test to save your job: the payoff
• Reentrance: If a task is interrupted, it can be restarted and completed successfully • Idempotence: A task can be called multiple times, without changing the side effects • Concurrence: More than one of a task can be run at the same time • Sequence Independence: The order of the tasks doesn’t matter Scalable tasks
$ • Easy multi-worker spinup • No fears/stress about re-running jobs or crons • Reduces fragility —less centering everything around one server being in one particular state Scalable tasks: the payoff
• Once you start seeding, it’s tempting to seed everything up front, once, for all your tests • It’s OK to seed some broad data up front—“company”, “user”—but uniquely seed the data under test in the test setup itself • Factory states and stories can be helpful here Seed each test
$ • No magic numbers • No need to flush work affected by previous tests • No “it works when we run this other test before but not when we don’t” • Data and tests on that data are in the same file, so they’re easier to connect and reason about Seed each test: the payoff
• Catchall for “POPO (Plain Old PHP Object)” that does stuff without being specifically tied to a design pattern or framework structure • Get creative with your naming (e.g. RetrieveMoviePoster) • Consider __invoke(): RetrieveMoviePoster() Service Classes
View Data Composition: Presenters class SignUpRequest extends Request { public function prepared() { // Do stuff here with the data } } // In controller: public function store(SignUpRequest $request) { $data = $request->prepared(); // ... etc. }
• Many oversized controllers/controller methods are because of the cost of organizing data for a specific view • Presenter: Layer on top of other thing, often model, to give it more powers in views (like a decorator for views) • View model: View POPO for organizational purposes • Responsable: interface in Laravel for a PHP object of any class that can be converted to a response • View components: idea/package for Vue-component- style view data composition View Data Composition
View Data Composition: Presenters class PostPresenter { public function __construct(Post $post) { $this->post = $post; } public function presentPublishedAt() { return $this->published_at->format('M j, Y'); } // Use custom methods, *or* use a presenter package to get magic // Eloquent-style accessors } // In controller: return view('posts.show')->with('post', new PostPresenter($post));
View Data Composition: View Models class DashboardViewModel { public function __construct(User $user) { $this->user = $user; } public function topTasks() { $overdue = $this->overdueTasks(); return array_merge($overdue, $this->fillTasksDueSoon(count($overdue))); } public function progressChart() { return [ 'percentage_complete' => $this->progressPercentageComplete(), 'date_markers' => $this->dateMarkers(), ]; } ... // In use: return view('dashboard')->with('vm', new DashboardViewModel($user));
View Data Composition: Responsables class DashboardViewResponse implements Responsable { //... other prep the same as before in the view model public function prepareData() { return [ 'tasks' => $this->tasks, // etc. ]; } public function toResponse() { $data = $this->prepareData(); return response()->view('dashboard', $data); } // In use (in the controller): return new DashboardViewResponse($user);
View Data Composition: Responsables class DashboardViewResponse implements Responsable { //... other prep the same as before in the view model public function prepareData() { return [ 'tasks' => $this->tasks, // etc. ]; } public function toResponse() { $data = $this->prepareData(); if (request()->ajax()) { return response()->json($data); } return view('dashboard', $data); } // In use (in the controller): return new DashboardViewResponse($user);
$ • Slimmer controllers • Clearer grouping and composition of data and its sources • Easier testing • Fun words like “Responsable” and “Htmlable” View Data Composition: the payoff