# GameChanger

This is GameChanger Technology.

This is one page of public article previews, not the complete archive. Follow Next page to continue. Summaries are not the original full articles.

## GameChanger and Anvil

DevFeed: [GameChanger and Anvil](<https://devfeed.tech/articles/gamechanger-and-anvil-19834.md>)

Original publisher: [Read original article](<https://tech.gc.com/gamechanger-and-anvil/>)

Author: GameChanger

Published: 2024-09-18T04:00:00Z

Content type: article

Language: en

Sources: [GameChanger](<https://devfeed.tech/sources/gamechanger.md>)

Topics: [Dagger](<https://devfeed.tech/topics/dagger.md>), [Dependency injection](<https://devfeed.tech/topics/dependency-injection.md>), [Android](<https://devfeed.tech/topics/android.md>), [Architecture & Design](<https://devfeed.tech/topics/architecture-design.md>), [Code quality](<https://devfeed.tech/topics/code-quality.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [code-quality](<https://devfeed.tech/tags/code-quality.md>), [dagger](<https://devfeed.tech/tags/dagger.md>), [dependency-injection](<https://devfeed.tech/tags/dependency-injection.md>), [development-tools](<https://devfeed.tech/tags/development-tools.md>), [workflows](<https://devfeed.tech/tags/workflows.md>)

### AI overview

GameChanger describes how its App Platform team integrates Anvil with an existing Dagger setup in an Android codebase. The effort streamlines dependency injection and supports modularization, consistent architecture, reduced cognitive load, faster onboarding, and improved code quality.

### Source excerpt

At GameChanger, we have a dedicated App Platform team that focuses on scaling our engineering organization and supporting development teams across the company. Our mission is to make building things the right way the easy way for all developers at GameChanger. This involves: Implementing and maintaining core infrastructure like dependency injection systems Creating and improving development tools and workflows Establishing best practices and architectural patterns Providing support and guidance to feature teams By having a dedicated team focus on these foundational aspects, we achieve several benefits: Consistency across the codebase, making it easier for developers to switch between projects Reduced cognitive load for feature teams, allowing them to focus on business logic Faster onboarding for new team members Improved code quality and maintainability The work on integrating Anvil with our existing Dagger setup is a prime example of how the App Platform team contributes to these goals. By streamlining our dependency injection process, we're making it easier for all teams to build features efficiently and correctly. With this context in mind, let's explore the technical details of our setup and why we chose to augment Dagger with Anvil. What's the setup? The Android codebase has the following Dagger components: a longstanding Application Component, a User Component that lives for as long as the user is logged in, and Game Components that last for as long as any live game. Features usually involve a fragment to render UI and a view model to manage business logic and state. Said view model is supplied by a factory in either the Application Component or User Component and pulls in dependencies from the rest of the Dagger graph that may come from any of the mentioned components. So Why Anvil? 1. Anvil makes modularization easy: We're following, as shown in the diagram above, a format for modules across the codebase to follow that involve the inversion of control princi

## Every Camera, Every Angle on Android

DevFeed: [Every Camera, Every Angle on Android](<https://devfeed.tech/articles/every-camera-every-angle-on-android-19832.md>)

Original publisher: [Read original article](<https://tech.gc.com/every-camera-every-angle-on-android/>)

Author: GameChanger

Published: 2021-09-20T14:49:50Z

Content type: tutorial

Language: en

Sources: [GameChanger](<https://devfeed.tech/sources/gamechanger.md>)

Topics: [Android](<https://devfeed.tech/topics/android.md>), [API](<https://devfeed.tech/topics/api.md>), [Streaming](<https://devfeed.tech/topics/streaming.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [apis](<https://devfeed.tech/tags/apis.md>), [article](<https://devfeed.tech/tags/article.md>), [camera](<https://devfeed.tech/tags/camera.md>), [camera2](<https://devfeed.tech/tags/camera2.md>), [camerax](<https://devfeed.tech/tags/camerax.md>), [streaming](<https://devfeed.tech/tags/streaming.md>), [ultra-wide](<https://devfeed.tech/tags/ultra-wide.md>), [video](<https://devfeed.tech/tags/video.md>), [video-streaming](<https://devfeed.tech/tags/video-streaming.md>), [wide](<https://devfeed.tech/tags/wide.md>)

### AI overview

This article explains how GameChanger implemented ultrawide camera streaming on Android. It describes the differences between physical and logical camera setups and the challenges of selecting the correct camera sensor through Android's multi-camera APIs.

### Source excerpt

At GameChanger, video streaming has become a huge part of our business and thus our tech stack. But as a small company that practices shipping often, we can't ship everything feature complete from day one and thus video streaming launched with the ability to only stream from your default rear camera lens. But as we know, ultrawide lenses on phones have become common place and sure enough, customers began writing in, asking to be able to use their ultrawide cameras to stream their event. Baseball and softball fields are actually quite wide and it makes a lot of sense to be able to capture more of the field. So in time, ultrawide streaming became our priority and thus we engaged in battle with one of the most brittle Android APIs we have seen... Streaming in the olden days Well, not really in the olden days, because we are using the most up to date APIs, but before we implemented ultrawide streaming, selecting the camera we wanted to stream with was generally pretty simple: private fun CameraManager.chooseCamera(teamId: TeamId) = cameraIdList.filter { id -> val characteristics = getCameraCharacteristics(id) val capabilities = characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)!! characteristics.get(CameraCharacteristics.LENS_FACING) == CameraMetadata.LENS_FACING_BACK && capabilities.contains(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE) } .mapNotNull { id -> val characteristics = getCameraCharacteristics(id) val cameraConfig = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)!! val (width, height) = arrayOf(1280, 720) cameraConfig.getOutputSizes(MediaRecorder::class.java) .filter { it.width <= width && it.height <= height } .maxByOrNull { it.width * it.height }?.let { id to it } } .map { (id, resolution) -> CameraArgs(cameraId = id, width = resolution.width, height = resolution.height, fps = 30) } .firstOrNull() TL;DR: Basically, get the first rear camera that supports 720p. Note the cameraId--an

## Improving User Experience by Doing the Big Refactor

DevFeed: [Improving User Experience by Doing the Big Refactor](<https://devfeed.tech/articles/improving-user-experience-by-doing-the-big-refactor-19835.md>)

Original publisher: [Read original article](<https://tech.gc.com/how-we-improved-user-experience-by-doing-the-big-refactor/>)

Author: GameChanger

Published: 2021-05-13T17:33:18Z

Content type: article

Language: en

Sources: [GameChanger](<https://devfeed.tech/sources/gamechanger.md>)

Topics: [User Experience](<https://devfeed.tech/topics/user-experience.md>), [App](<https://devfeed.tech/topics/app.md>), [data](<https://devfeed.tech/topics/data.md>), [Database](<https://devfeed.tech/topics/database.md>)

Tags: [analysis](<https://devfeed.tech/tags/analysis.md>), [app](<https://devfeed.tech/tags/app.md>), [blog-post](<https://devfeed.tech/tags/blog-post.md>), [complexity](<https://devfeed.tech/tags/complexity.md>), [cost](<https://devfeed.tech/tags/cost.md>), [customers](<https://devfeed.tech/tags/customers.md>), [data](<https://devfeed.tech/tags/data.md>), [database](<https://devfeed.tech/tags/database.md>), [launch](<https://devfeed.tech/tags/launch.md>), [project](<https://devfeed.tech/tags/project.md>), [refactor](<https://devfeed.tech/tags/refactor.md>), [software](<https://devfeed.tech/tags/software.md>), [technical](<https://devfeed.tech/tags/technical.md>), [tests](<https://devfeed.tech/tags/tests.md>), [user-experience](<https://devfeed.tech/tags/user-experience.md>), [ux](<https://devfeed.tech/tags/ux.md>)

### AI overview

The article describes GameChanger's decision to undertake a major refactor of its data model. The project replaced a foundational database entity with new entities to address user confusion and editing restrictions in Team Manager, with the goal of improving the user experience.

### Source excerpt

In a long lived and complex application, there often comes a time when some early decisions end up becoming constraints that are difficult or impossible to overcome. At some point in the lifespan of the software, you may be faced with a choice -- do the Big Refactor, or find ways to work around the problems. More often than not, the latter route is taken. When you have a lot of immediate priorities, it's difficult to justify spending a lot of time on a big and potentially risky project. Sometimes however, the benefits of doing the big refactor greatly outweigh the drawbacks. It takes a cost-benefit analysis specific to your app to determine if that is the case. And recently at GameChanger, the big refactor was worth it, so we did it! This blog post is about how we at GameChanger successfully executed a huge overhaul to our data model. We removed a foundational database entity, person, upon which almost every part of our system relied in some way, and replaced it with new entities which enabled the user experience that we knew our customers expected. We'll take a look at what user-facing problem we were addressing and the high level adjustment to our data model. We'll also talk about how we approached such a large project tactically, some of the interesting technical problems we solved along the way, and the benefits we're reaping a couple months out from shipping. The Problem 🧩 For a long time, one of our most common complaints from users of Team Manager was the complexity around who can edit players and what information they could edit. The root of the complexity was this: a player could be created without an email address, but, once a player was attached to an email address, a user could not edit that player's information, like first name or last name, independently of another team. A player's name and their relationships to other users would carry over from team to team. And that meant in order to avoid situations where a coach on one team could make edits to a pl

## Let me automate that for you II, Electric Bugaloo

DevFeed: [Let me automate that for you II, Electric Bugaloo](<https://devfeed.tech/articles/let-me-automate-that-for-you-ii-electric-bugaloo-19838.md>)

Original publisher: [Read original article](<https://tech.gc.com/let-me-automate-that-for-you-ii-electric-bugaloo/>)

Author: GameChanger

Published: 2021-05-07T18:29:38Z

Content type: article

Language: en

Sources: [GameChanger](<https://devfeed.tech/sources/gamechanger.md>)

Topics: [SQL](<https://devfeed.tech/topics/sql.md>), [Amazon Redshift](<https://devfeed.tech/topics/amazon-redshift.md>), [Pull Request](<https://devfeed.tech/topics/pull-request.md>), [Optimization](<https://devfeed.tech/topics/optimization.md>), [Structured-data](<https://devfeed.tech/topics/structured-data.md>), [Script](<https://devfeed.tech/topics/script.md>), [Slack](<https://devfeed.tech/topics/slack.md>), [Documentation](<https://devfeed.tech/topics/documentation.md>)

Tags: [automation](<https://devfeed.tech/tags/automation.md>), [blog](<https://devfeed.tech/tags/blog.md>), [blog-post](<https://devfeed.tech/tags/blog-post.md>), [building](<https://devfeed.tech/tags/building.md>), [data](<https://devfeed.tech/tags/data.md>), [documentation](<https://devfeed.tech/tags/documentation.md>), [github](<https://devfeed.tech/tags/github.md>), [kafka](<https://devfeed.tech/tags/kafka.md>), [optimization](<https://devfeed.tech/tags/optimization.md>), [performance](<https://devfeed.tech/tags/performance.md>), [pipeline](<https://devfeed.tech/tags/pipeline.md>), [reactive](<https://devfeed.tech/tags/reactive.md>), [redshift](<https://devfeed.tech/tags/redshift.md>), [sql](<https://devfeed.tech/tags/sql.md>), [systems](<https://devfeed.tech/tags/systems.md>), [warehouse](<https://devfeed.tech/tags/warehouse.md>)

### AI overview

The article describes evolving an embedded SQL generator and related scripts into a standalone SQL producer for warehouse schema and table management. The system generates SQL migrations, creates or updates tables, optimizes table performance, documents proposed changes, opens pull requests, and notifies engineers in Slack for review. The article also discusses limitations of the reactive original implementation and outlines the design of the improved system.

### Source excerpt

Improving our original, embedded SQL generator and some related scripts by converting them to a better, long term, stand alone SQL producer that's faster, more reliable, and more obvious. About seventeen years ago, in 2019, I published my blog post "Let me automate that for you" about a design for automating creating warehouse tables based on schemas for new event data. The idea was when our ETL system couldn't load waiting data into a warehouse table (as there was no table to be found), it would look up the schema for that data, convert the schema to a SQL statement, then issue a PR to the repo where SQL migrations for such needs are kept. Eventually creating tables made a friend, updating tables when there was a mismatch between the schema of the data we were loading and the schema of the table in the warehouse, and a third buddy joined the part, optimizing a table to improve its performance. The system had some absolutely great qualities: it automated acting on errors it saw, it generated great documentation in the PR and the SQL statement (with comments for discussions and places to review more closely), and it posted to Slack to let engineers know that there was something for them to do a final review on. However... it wasn't perfect. Reading is going toward something that is about to be, and no one yet knows what it will be. [1] Let me take you through the evolution of our embedded SQL generator to stand-alone SQL producer. Limitations of previous implementation Opportunities to build it better Building blocks of a stand-alone SQL producer Joining the human needs with the computer's logic Detailed breakdown of the services available Troubleshooting live Final thoughts Appendix A: Redshift optimization queries Appendix B: select Github logic Limitations of previous implementation While the SQL generator eased so much work for so many different people in the company, it had some... strange caveats, shall we say. Some were more noticable than others but all were, in

## From pipeline to beyond

DevFeed: [From pipeline to beyond](<https://devfeed.tech/articles/from-pipeline-to-beyond-19833.md>)

Original publisher: [Read original article](<https://tech.gc.com/from-pipeline-to-beyond/>)

Author: GameChanger

Published: 2021-05-05T09:00:18Z

Content type: article

Language: en

Sources: [GameChanger](<https://devfeed.tech/sources/gamechanger.md>)

Topics: [Kafka](<https://devfeed.tech/topics/kafka.md>), [Amazon S3](<https://devfeed.tech/topics/amazon-s3.md>), [data](<https://devfeed.tech/topics/data.md>), [streaming-data-processing](<https://devfeed.tech/topics/streaming-data-processing.md>), [Back end](<https://devfeed.tech/topics/backend.md>)

Tags: [analysis](<https://devfeed.tech/tags/analysis.md>), [backend](<https://devfeed.tech/tags/backend.md>), [consul](<https://devfeed.tech/tags/consul.md>), [data](<https://devfeed.tech/tags/data.md>), [infrastructure](<https://devfeed.tech/tags/infrastructure.md>), [kafka](<https://devfeed.tech/tags/kafka.md>), [pipeline](<https://devfeed.tech/tags/pipeline.md>), [redshift](<https://devfeed.tech/tags/redshift.md>), [s3](<https://devfeed.tech/tags/s3.md>), [terraform](<https://devfeed.tech/tags/terraform.md>), [warehouse](<https://devfeed.tech/tags/warehouse.md>)

### AI overview

The article examines options for moving data out of Kafka into a data warehouse and archive. It discusses off-the-shelf tools such as Kafka Connect, Secor, and Gobblin, the limitations encountered, and the development of a custom solution. The requirements include preserving Avro data and schemas, writing to S3, and partitioning data by processing time or event time.

### Source excerpt

An overview of off-the-shelf solutions for moving data out of Kafka, problems we had making those systems work, and how we wrote our own solution and stood it up for those in a similar situation. You cannot know everything a system will be used for when you start: it is only at the end of its life you can have such certainty. [1] Many moons ago I wrote about our design for upgrading our data pipeline, which lightly touched on how we'd move data out of our pipeline (Kafka) to downstream systems, namely our data warehouse and data archive. At that time we hadn't really been able to dive into focusing on getting the data out of Kafka, because getting data in to Kafka is often much more custom and complex, and we thought we'd be able to use an off the shelf solution like Kafka Connect to move data out, don't even worry about it. We were, uh -- we were wrong. Let me take you on our journey, in case you're on this journey too. The problem space Solution 1: Kafka Connect Solution 2: Secor or Gobblin Solution 3: we'll do this ourselves Tangent: naming things How you can do this yourselves, code edition How you can do this yourselves, infrastructure and metrics edition Takeaways The problem space Programmers are not to be measured by their ingenuity and their logic but by the completeness of their case analysis. [4] At a high level, the problem we needed a solution for was as follows: Data enters the data pipeline from numerous backend systems. This crossover point is producers into the pipeline, which we'd already implemented. Data from the data pipeline needs to move into the data warehouse and the data archive. This crossover point would be a consumer on the pipeline. Ideally we'd like the same consumer for both needs that we can simply configure differently. We want to preserve our data's Avro format along side its schemas. This would allow every system that interacts with the data to use the same language. We want to write our data to S3. data warehouse: This will be our

## Capturing Script Logs

DevFeed: [Capturing Script Logs](<https://devfeed.tech/articles/capturing-script-logs-19827.md>)

Original publisher: [Read original article](<https://tech.gc.com/capturing-script-logs/>)

Author: GameChanger

Published: 2020-12-01T00:00:00Z

Content type: tutorial

Language: en

Sources: [GameChanger](<https://devfeed.tech/sources/gamechanger.md>)

Topics: [Script](<https://devfeed.tech/topics/script.md>), [Scripting, bash](<https://devfeed.tech/topics/scripting-bash.md>), [Processes](<https://devfeed.tech/topics/processes.md>), [Ansible](<https://devfeed.tech/topics/ansible.md>), [Slack](<https://devfeed.tech/topics/slack.md>), [SIEM, Security, Observability](<https://devfeed.tech/topics/siem-security-observability.md>)

Tags: [ansible](<https://devfeed.tech/tags/ansible.md>), [bash](<https://devfeed.tech/tags/bash.md>), [dev](<https://devfeed.tech/tags/dev.md>), [logs](<https://devfeed.tech/tags/logs.md>), [metrics](<https://devfeed.tech/tags/metrics.md>), [monitor](<https://devfeed.tech/tags/monitor.md>), [operations](<https://devfeed.tech/tags/operations.md>), [process](<https://devfeed.tech/tags/process.md>), [slack](<https://devfeed.tech/tags/slack.md>)

### AI overview

This tutorial explains how GameChanger captures script output and reports errors to Slack when DataDog integration is unavailable or its alerts lack sufficient context. It introduces the Bash tee command and process substitution as building blocks for capturing and forwarding script logs.

### Source excerpt

At GameChanger, we use scripts in many of our flows such as during deploys or running Ansible while booting new instances. Some of these flows are critical to our operations and require good visibility. Traditionally we tried to send both metrics and logs to DataDog where we can both monitor what scripts are doing and set alerts on various metrics. DataDog integration, however, is not always available such as during the instance boot. In addition, sometimes a DataDog alert does not provide enough context of what failed and most importantly why it failed. It simply states that some threshold was reached. To investigate the issue requires more manual steps by looking for the appropriate log which is not always intuitive. That is why recently at GameChanger we started integrating Slack error reporting directly into some of our critical scripts. This post describes the exact mechanism of how that is achieved since it uses a really cool bash trick. Useful Commands First some background on some of the useful commands. tee Anyone? Tee is a really useful command. It captures an output from a script and both echoes it to standard out as well as forwards it to a file. This allows to both see an output as well as capture the same output for later use. For example: $ echo hello there | tee hello.log hello there $ cat hello.log hello there Process Substitution Some commands only work with files. For example a classic diff: $ echo one > one.txt $ echo two > two.txt $ diff -u one.txt two.txt --- one.txt +++ two.txt @@ -1 +1 @@ -one +two Sometimes however it is convenient to be able to refer to an output of commands as a file without manually creating a temporary file. This is what process substitution allows to do. Same example as above but with process substitution: $ diff -u <(echo one) <(echo two) --- /dev/fd/63 2020-11-30 12:33:08.539905663 -0500 +++ /dev/fd/62 2020-11-30 12:33:08.540272967 -0500 @@ -1 +1 @@ -one +two This is the output form of process substitution which uses

## Dependency Injection in Typescript with tsyringe

DevFeed: [Dependency Injection in Typescript with tsyringe](<https://devfeed.tech/articles/dependency-injection-in-typescript-with-tsyringe-19830.md>)

Original publisher: [Read original article](<https://tech.gc.com/dependency-injection/>)

Author: GameChanger

Published: 2020-11-30T23:47:04Z

Content type: article

Language: en

Sources: [GameChanger](<https://devfeed.tech/sources/gamechanger.md>)

Topics: [Dependency injection](<https://devfeed.tech/topics/dependency-injection.md>), [TypeScript](<https://devfeed.tech/topics/typescript.md>), [Framework](<https://devfeed.tech/topics/framework.md>)

Tags: [dependencies](<https://devfeed.tech/tags/dependencies.md>), [dependency](<https://devfeed.tech/tags/dependency.md>), [dependency-injection](<https://devfeed.tech/tags/dependency-injection.md>), [framework](<https://devfeed.tech/tags/framework.md>), [tests](<https://devfeed.tech/tags/tests.md>), [typescript](<https://devfeed.tech/tags/typescript.md>)

### AI overview

This article explains dependency injection in TypeScript, including manual constructor-based injection and the use of mocks for testing. It then introduces Tsyringe as a dependency injection framework that provides injectable decorators and a dependency container for resolving classes.

### Source excerpt

Why Dependency Injection In any large object oriented codebase, managing dependencies can get difficult. Each class can require any number of third parties or other classes to function, and it can be hard to test the behavior of a single class with mocks if those dependencies aren't easy to provide. Fortunately, there's a popular design pattern that can be applied to solve this problem, and that is dependency injection. When using dependency injection, classes can be provided their dependencies through a constructor, and those dependencies can be swapped out easily for other implmentations. In tests, mocks can simply be substituted in to test class behavior. While most of the time, this pattern is implemented with a framework, even without one manual dependency injection can give you some of these benefits. Here at Gamechanger, we previously had a form of manual dependency injection in our typescript codebase. Each class would have a constructor that accepted its dependencies, which could be swapped out with mocks or a real implementation. class BusinessLogic { constructor (dependencyA: DependencyA, dependencyB: DependencyB) {} private void foo() { this.dependencyA.action(); } } // To instantiate this class, both dependencies must be created const businessLogic = new BusinessLogic(new DependencyA(), new DependencyB()); // to test this class, mocks can be passed in const testBusinessLogic = new BusinessLogic(new MockDependencyA(), new MockDependencyB()); In this example, if you need to replace a dependency, you can just supply a different class in the constructor for BusinessLogic. This can work great for a small number of classes with a tiny dependency tree, but as your codebase's number of dependencies grow, it can become difficult to manage. Once your dependencies have dependencies, its not as straightforward to get an instance of a class. class DependencyC { constructor(dependencyE, DepedencyE) { } } class DependencyA { constructor(dependencyC: DependencyC, depen

## Deploys at Gamechanger

DevFeed: [Deploys at Gamechanger](<https://devfeed.tech/articles/deploys-at-gamechanger-19831.md>)

Original publisher: [Read original article](<https://tech.gc.com/deploys-at-gamechanger/>)

Author: GameChanger

Published: 2020-07-28T22:24:51Z

Content type: article

Language: en

Sources: [GameChanger](<https://devfeed.tech/sources/gamechanger.md>)

Topics: [Deployment](<https://devfeed.tech/topics/deployment.md>), [Docker](<https://devfeed.tech/topics/docker.md>), [Amazon EC2](<https://devfeed.tech/topics/amazon-ec2.md>), [ci](<https://devfeed.tech/topics/ci.md>), [Dockerfile](<https://devfeed.tech/topics/dockerfile.md>), [Docker Image](<https://devfeed.tech/topics/docker-image.md>), [Protocol (disambiguation)](<https://devfeed.tech/topics/protocol.md>)

Tags: [build](<https://devfeed.tech/tags/build.md>), [ci](<https://devfeed.tech/tags/ci.md>), [container](<https://devfeed.tech/tags/container.md>), [deployment](<https://devfeed.tech/tags/deployment.md>), [docker](<https://devfeed.tech/tags/docker.md>), [docker-images](<https://devfeed.tech/tags/docker-images.md>), [images](<https://devfeed.tech/tags/images.md>), [load-balancer](<https://devfeed.tech/tags/load-balancer.md>), [protocol](<https://devfeed.tech/tags/protocol.md>)

### AI overview

This article describes GameChanger's deployment pipeline. Each commit triggers CI to build and tag a Docker image, push it to an internal Docker registry, and deploy it to relevant Amazon EC2 instances. The instances start containers, register with a load balancer, and receive deployment notifications through Serf's Gossip protocol.

### Source excerpt

At GameChanger, being able to deploy our changes quickly and reliably has always been important. Over the past few months on the platform team, we've been working to build a simpler and more reliable deployment pipeline to support our product teams in shipping code with speed and reliability. In this post, I'll go over the system that we have in place now, and some of our recent improvements. How deploys our work High Level Overview At a high level, the core of our deployment process involves shipping a docker image to some EC2 instances, and then starting a container with that image. Our main code repository contains a Dockerfile with the required configuration for our live app. After each new commit to our main code repository, Our CI process will docker build a new image with a new tag, and push it to our internal docker registry. During a deploy, we pull that image on the relevant EC2 instances, and start a container with it. Once we have a running container, we register that EC2 instance with a load balancer, and start serving traffic. While the high level is straight forward, there's a lot of work that goes into making sure our deployments happen safely, and at the right time. Once a new image has been pushed to our registry by our CI process, we need to notify all our currently running instances that there's a new version to deploy. Each commit corresponds to a new docker tag that's created during the build. Once the tag is created, we notify our internal deployment service of the new version. Once our tag is pushed, and our deployment service is aware of it, we tell our deployment service to start a deploy, which will send a message to all of our running EC2 instances. Serf Messages We use Serf to send that message out without needing to directly contact every instance. The message we send contains a role and environment. The role is the name of a docker image we want to deploy a new version of, and environment will be production or staging depending on wher

## Apache Airflow on AWS ECS

DevFeed: [Apache Airflow on AWS ECS](<https://devfeed.tech/articles/apache-airflow-on-aws-ecs-19822.md>)

Original publisher: [Read original article](<https://tech.gc.com/apache-airflow-on-aws-ecs/>)

Author: GameChanger

Published: 2020-07-06T09:00:18Z

Content type: tutorial

Language: en

Sources: [GameChanger](<https://devfeed.tech/sources/gamechanger.md>)

Topics: [airflow](<https://devfeed.tech/topics/airflow.md>), [Amazon Web Services](<https://devfeed.tech/topics/aws.md>), [Containers](<https://devfeed.tech/topics/containers.md>), [Jenkins](<https://devfeed.tech/topics/jenkins.md>), [data](<https://devfeed.tech/topics/data.md>), [Pull Request](<https://devfeed.tech/topics/pull-request.md>)

Tags: [airflow](<https://devfeed.tech/tags/airflow.md>), [aws](<https://devfeed.tech/tags/aws.md>), [containers](<https://devfeed.tech/tags/containers.md>), [data](<https://devfeed.tech/tags/data.md>), [ecs](<https://devfeed.tech/tags/ecs.md>), [jenkins](<https://devfeed.tech/tags/jenkins.md>), [jobs](<https://devfeed.tech/tags/jobs.md>), [kubernetes](<https://devfeed.tech/tags/kubernetes.md>), [pull-request](<https://devfeed.tech/tags/pull-request.md>), [workflow](<https://devfeed.tech/tags/workflow.md>)

### AI overview

This tutorial explains how a data team moved from Jenkins to Apache Airflow and ran Airflow and other containerized workloads on AWS ECS. It introduces ECS clusters, task definitions, services, and deployment choices including Fargate, EC2, and Kubernetes, while noting that Airflow infrastructure is straightforward but configuring the ECS operator requires substantial setup.

### Source excerpt

An overview of what AWS ECS is, how to run Apache Airflow and tasks on it for eased infrastructure maintenance, and what we've encountered so that you have an easier time getting up and running. Our data team recently made the transition in workflow systems from Jenkins to Apache Airflow. Airflow was a completely new system to us that we had no previous experience with but is the current industry standard for the sort of data-centric workflow jobs we were looking to run. While it hasn't been a perfect fit, we have been able to get a lot of benefits from it: jobs are defined in code, we've the history of each job, it goes through our normal pull request process, and everyone on the team is able to read and write jobs. Since our team is data focused, we wanted our Airflow setup to be as easy to maintain as possible, especially around infrastructure, so we have minimal distractions with high resiliency. This led us to using AWS ECS not only to run Airflow but for our bigger tasks that are already containerized. Not familiar with ECS? Or how to run Airflow or its tasks on it? Don't worry, we weren't either. This is what we've learned. What is ECS? ECS with Terraform Setting up Airflow infrastructure on ECS Setting up Airflow tasks on ECS Learnings from Airflow and ECS tl;dr Running Airflow's infrastructure on ECS is super easy but running the ECS operator needs hecka help setting up. What is ECS? ECS is AWS's Elastic Container Service, designed to let you run containers without worrying about servers. You do this by creating a cluster for your system, define task definitions for the tasks you want to run, and possibly group your tasks into services. You can also choose if you want to have your containers fully managed (ECS on Fargate), kind of managed but kind of not (ECS on EC2), or if you'd like to use Kubernetes. If all of that made sense to you, congratulations: you can skip to the next section! If you need a bit more of a breakdown, here's what you need to know: A

## Crash Course to Redshift

DevFeed: [Crash Course to Redshift](<https://devfeed.tech/articles/crash-course-to-redshift-19828.md>)

Original publisher: [Read original article](<https://tech.gc.com/crash-course-to-redshift/>)

Author: GameChanger

Published: 2020-03-30T14:36:13Z

Content type: tutorial

Language: en

Sources: [GameChanger](<https://devfeed.tech/sources/gamechanger.md>)

Topics: [Amazon Redshift](<https://devfeed.tech/topics/amazon-redshift.md>), [Databases](<https://devfeed.tech/topics/databases.md>), [data](<https://devfeed.tech/topics/data.md>), [IO](<https://devfeed.tech/topics/io.md>), [Optimization](<https://devfeed.tech/topics/optimization.md>), [Development](<https://devfeed.tech/topics/development.md>), [debugging](<https://devfeed.tech/topics/debugging.md>)

Tags: [data](<https://devfeed.tech/tags/data.md>), [database](<https://devfeed.tech/tags/database.md>), [debugging](<https://devfeed.tech/tags/debugging.md>), [development](<https://devfeed.tech/tags/development.md>), [distributed](<https://devfeed.tech/tags/distributed.md>), [documentation](<https://devfeed.tech/tags/documentation.md>), [how-to](<https://devfeed.tech/tags/how-to.md>), [io](<https://devfeed.tech/tags/io.md>), [optimization](<https://devfeed.tech/tags/optimization.md>), [performance](<https://devfeed.tech/tags/performance.md>), [redshift](<https://devfeed.tech/tags/redshift.md>), [scale](<https://devfeed.tech/tags/scale.md>), [warehouse](<https://devfeed.tech/tags/warehouse.md>)

### AI overview

A crash course to Amazon Redshift that introduces its database architecture, large-scale table design considerations, data distribution, data loading, debugging, and query-performance optimization.

### Source excerpt

Redshift. It can store insane amounts of data. It can also store insane amounts of surprises, considerations, new ideas to learn, skewed tables to fix, distributions to get in line, what's a WLM, what am I doing‽ This post is meant to give you a crash course into working with Redshift, to get you off and running until you have the time and resources to come back and internalize what it all means. This is by no means a comprehensive review of Redshift, as then it'd no longer be a crash course, nor does this dive into data warehousing specifics, which I can cover in another post if people want. At a high level what I'll be covering is: Introduction to Redshift Table design Table analysis Data loading Debugging The vast majority of this post actually comes from our internal documentation, so you can trust that we do use this to help educate those less familiar with Redshift, and get them ramped up and feeling comfortable. Introduction to Redshift On Redshift The Redshift database will behave like other databases you've encountered, but under the hood it has some extra considerations to take into account. The main difference between Redshift and most other databases you'll have encountered is due to scale, with the cluster being important to keep in mind in table design along with standard table design considerations. And since the scale is so much larger, the impact of IO can go up considerably, especially if the cluster needs to move or share data to perform a query. The reasons for this and how to best avoid these inefficiencies are detailed below. More on Redshift database development here. On distributing data Within a Redshift cluster, there is a leader node and many compute nodes. The leader node helps orchestrate the work the compute nodes do. For example, if a query is operating only on data from May of 2017, and all of that data is stored on a single compute node, the leader only needs that node to perform the work. If instead a query is operating on data from

## Monitoring Node.js Using Datadog APM

DevFeed: [Monitoring Node.js Using Datadog APM](<https://devfeed.tech/articles/monitoring-node-js-using-datadog-apm-19840.md>)

Original publisher: [Read original article](<https://tech.gc.com/monitoring-node-js-using-datadog-apm/>)

Author: GameChanger

Published: 2020-01-29T19:36:34Z

Content type: tutorial

Language: en

Sources: [GameChanger](<https://devfeed.tech/sources/gamechanger.md>)

Topics: [Application Performance Management (APM)](<https://devfeed.tech/topics/apm.md>), [Node.js](<https://devfeed.tech/topics/node-js.md>), [observability](<https://devfeed.tech/topics/observability.md>), [Monitoring](<https://devfeed.tech/topics/monitoring.md>), [API](<https://devfeed.tech/topics/api.md>), [dashboards](<https://devfeed.tech/topics/dashboards.md>), [Traces](<https://devfeed.tech/topics/traces.md>), [Logging](<https://devfeed.tech/topics/logging.md>)

Tags: [api](<https://devfeed.tech/tags/api.md>), [apm](<https://devfeed.tech/tags/apm.md>), [dashboards](<https://devfeed.tech/tags/dashboards.md>), [datadog](<https://devfeed.tech/tags/datadog.md>), [logging](<https://devfeed.tech/tags/logging.md>), [metrics](<https://devfeed.tech/tags/metrics.md>), [node](<https://devfeed.tech/tags/node.md>), [node-js](<https://devfeed.tech/tags/node-js.md>), [observability](<https://devfeed.tech/tags/observability.md>), [traces](<https://devfeed.tech/tags/traces.md>)

### AI overview

This article explains how GameChanger integrated Datadog APM to diagnose performance issues in a high-volume Node.js API server. It describes APM concepts, including request-level tracing, function calls, database queries, response tracking, dashboards, logs, and infrastructure monitoring, and briefly discusses the supporting Node.js client library and open-sourced utility code.

### Source excerpt

Intro GameChanger's Team Manager is built on top of a Node.js API server. Although we've had a lot of monitoring and logging throughout it's lifetime, most of our metrics were at an aggregate level. By alerting on symptoms, it was easy to spot that something was broken, but not necessarily what or why. With the server handling over 100 million requests per day, over 1000 per second, we needed a better way to drill into what the server was doing and figure out causes. Operating at that scale meant that local testing was at best an approximation of production realities. Integrating Datadog APM (Application Performance Monitoring) has solved a lot of our observability issues. It wasn't always straightforward to work with--some of the tooling isn't obvious and the documentation is all over the place--but now that things are up and running, it'd be hard to imagine a world without it. We've also open sourced some of the APM utility code we made along the way as an NPM package. Outline What is APM? What does Datadog provide? What challenges did we face? What doesn't Datadog provide? A feature wishlist Introducing @gamechanger/datadog-apm Conclusion What is APM? APM Stands for Application Performance Monitoring. It can take different forms from one offering to another, but it generally consists of breaking down what your server is doing while accomplishing a specific unit of work. For example, if your server offers an API, the APM will keep track of when a request starts, what functions are called to handle that request, what database queries are performed, and what the response is. A representative flame chart from Datadog's APM for one API request. In Datadog's words, APM "provides you with deep insight into your application's performance - from automatically generated dashboards for monitoring key metrics, like request volume and latency, to detailed traces of individual requests - side by side with your logs and infrastructure monitoring". What does Datadog provide? In ad

## Using Terraform, Consul, and Ansible to Define and Deploy a Data Pipeline

DevFeed: [Using Terraform, Consul, and Ansible to Define and Deploy a Data Pipeline](<https://devfeed.tech/articles/i-ll-say-this-exactly-once-19836.md>)

Original publisher: [Read original article](<https://tech.gc.com/ill-say-this-exactly-once/>)

Author: GameChanger

Published: 2019-12-18T12:00:00Z

Content type: tutorial

Language: en

Sources: [GameChanger](<https://devfeed.tech/sources/gamechanger.md>)

Topics: [Infrastructure as code](<https://devfeed.tech/topics/infrastructure-as-code.md>), [Ansible](<https://devfeed.tech/topics/ansible.md>), [Terraform](<https://devfeed.tech/topics/terraform.md>), [configuration](<https://devfeed.tech/topics/configuration.md>), [JSON](<https://devfeed.tech/topics/json.md>), [YAML](<https://devfeed.tech/topics/yaml.md>), [Git](<https://devfeed.tech/topics/git.md>), [systems](<https://devfeed.tech/topics/systems.md>)

Tags: [ansible](<https://devfeed.tech/tags/ansible.md>), [collection](<https://devfeed.tech/tags/collection.md>), [consul](<https://devfeed.tech/tags/consul.md>), [data](<https://devfeed.tech/tags/data.md>), [git](<https://devfeed.tech/tags/git.md>), [iac](<https://devfeed.tech/tags/iac.md>), [infrastructure](<https://devfeed.tech/tags/infrastructure.md>), [infrastructure-as-code](<https://devfeed.tech/tags/infrastructure-as-code.md>), [json](<https://devfeed.tech/tags/json.md>), [pipeline](<https://devfeed.tech/tags/pipeline.md>), [terraform](<https://devfeed.tech/tags/terraform.md>), [yaml](<https://devfeed.tech/tags/yaml.md>)

### AI overview

The article explains how infrastructure as code can document and reproduce a data pipeline across environments. It describes combining Terraform, Consul, and Ansible so infrastructure, machine configuration, and related settings can be managed together.

### Source excerpt

You've finally got everything working in your staging environment: the new systems talk to each other, everything is running smoothly, your dashboards are beautiful and pristine. Now you need to get it into production with zero down time and no interruptions. And then you realize, you've no idea what you actually did in your staging environment. Let's talk about infrastructure I don't consider myself an "infrastructure lady;" it's just not my jam, and that's fine. However I have had to learn a lot about our infrastructure for setting up our new data pipeline system -- and I don't just show up to learn the minimum. I come to m a s t e r. IaC, or infrastructure as code, is the idea of setting up your infrastructure using definition files, code, and standard programming practices. In a way, it brings infrastructure to those of us who might otherwise be overwhelmed by what's going on and what we're suppose to do. Why manually enter configuration values when you can store them in a YAML or JSON file in a git repo? Suddenly you can see its history, you can search it, and it documents itself to a certain extent. This last point is of particular interest to me, as so much of the data pipeline is just how the systems work together, what the configurations are to facilitate this, and and documenting how all this works. If -- if -- we could have our data pipeline and all its friends live in some straightforward, self documenting IaC setup, it would make it easy not only to remember what we did but also to onboard someone new to the system and to deploy it to different environments with high confidence that it'll work. At GameChanger, that meant making three systems work together: Terraform, Consul, and Ansible. Terraform sets up what we want the landscape of our machines to look like. Ansible sets up what we want the landscape on our machines to look like. And Consul is the new kid in the collection, just here to have a good time and be helpful. Leibnizian optimism Alright, so we

## When you deserve better (systems)

DevFeed: [When you deserve better (systems)](<https://devfeed.tech/articles/when-you-deserve-better-systems-19844.md>)

Original publisher: [Read original article](<https://tech.gc.com/when-you-deserve-better-systems/>)

Author: GameChanger

Published: 2019-10-25T09:00:00Z

Content type: article

Language: en

Sources: [GameChanger](<https://devfeed.tech/sources/gamechanger.md>)

Topics: [Kafka](<https://devfeed.tech/topics/kafka.md>), [data](<https://devfeed.tech/topics/data.md>), [systems](<https://devfeed.tech/topics/systems.md>), [event driven](<https://devfeed.tech/topics/event-driven.md>)

Tags: [architecture](<https://devfeed.tech/tags/architecture.md>), [blog](<https://devfeed.tech/tags/blog.md>), [clusters](<https://devfeed.tech/tags/clusters.md>), [data](<https://devfeed.tech/tags/data.md>), [documentation](<https://devfeed.tech/tags/documentation.md>), [event-driven](<https://devfeed.tech/tags/event-driven.md>), [infrastructure](<https://devfeed.tech/tags/infrastructure.md>), [kafka](<https://devfeed.tech/tags/kafka.md>), [logs](<https://devfeed.tech/tags/logs.md>), [overview](<https://devfeed.tech/tags/overview.md>), [pipeline](<https://devfeed.tech/tags/pipeline.md>), [systems](<https://devfeed.tech/tags/systems.md>), [technical](<https://devfeed.tech/tags/technical.md>)

### AI overview

A technical blog post revisits a data pipeline and data warehouse architecture built around Kafka and Zookeeper. It describes how the system supported warehouse growth, business intelligence, and analysis, while infrastructure, Kafka capabilities, and data consumers changed over time.

### Source excerpt

Preface Before we begin, I suggest settling in. Unlike my previous post, this one won't be short. It'll also be more technical, though I will do my best to link to resources in case you get lost along the way. So my suggestion is get a nice cuppa, turn off notifications, and brace yourself: We're going on an adventure. In the beginning Back in 2016, when we were all younger and more innocent, Alex Etling wrote a series of blog posts about his learnings in setting up our original data pipeline, which was built around Kafka, which is built upon Zookeeper. I'll let his blog posts speak for themselves in case you're interested in that history, though they're not necessary prerequisites for this post if you'd rather save them for later. Fun With Kafka: Adding a New Box Type Part 1 and Part 2 Experimenting With Kafka Scaling With Kafka Instead to give you a lay of the land, here's a high level overview of the data pipeline and data warehouse systems, to function as a map for our adventure: Bringing back this architecture diagram, as it's so wonderful. [1] Spiffy, huh? Thus the Kafka and the Zookeeper clusters, and all the host of them, were finished This pipeline setup has allowed us to do things like grow our data warehouse, provide business intelligence to data users, and do deep analysis as a data team to answer questions about how we can better serve our customers. At the same time, a lot has changed around this system: in our infrastructure setup, in Kafka's capabilities, and in who uses the pipeline and for what. Back then, the desire was to answer a few basic questions about the customers using a variety of data; now the consumers of our data go in hard to look at the nitty gritty details themselves and really understand the complexity in the data to get their answers. What makes a team successful? Are our features helpful to teams across sports and ages? The people need to know. You cannot know everything a system will be used for when you start: it is only at the

## Let me automate that for you

DevFeed: [Let me automate that for you](<https://devfeed.tech/articles/let-me-automate-that-for-you-19839.md>)

Original publisher: [Read original article](<https://tech.gc.com/let-me-automate-that-for-you/>)

Author: GameChanger

Published: 2019-09-20T18:29:38Z

Content type: article

Language: en

Sources: [GameChanger](<https://devfeed.tech/sources/gamechanger.md>)

Topics: [data](<https://devfeed.tech/topics/data.md>), [systems](<https://devfeed.tech/topics/systems.md>), [Refactoring](<https://devfeed.tech/topics/refactoring.md>), [Back end](<https://devfeed.tech/topics/backend.md>), [Database](<https://devfeed.tech/topics/database.md>), [SQL](<https://devfeed.tech/topics/sql.md>)

Tags: [architecture](<https://devfeed.tech/tags/architecture.md>), [automation](<https://devfeed.tech/tags/automation.md>), [backend](<https://devfeed.tech/tags/backend.md>), [data](<https://devfeed.tech/tags/data.md>), [database](<https://devfeed.tech/tags/database.md>), [pipeline](<https://devfeed.tech/tags/pipeline.md>), [refactor](<https://devfeed.tech/tags/refactor.md>), [refactoring](<https://devfeed.tech/tags/refactoring.md>), [sql](<https://devfeed.tech/tags/sql.md>), [tests](<https://devfeed.tech/tags/tests.md>), [warehouse](<https://devfeed.tech/tags/warehouse.md>)

### AI overview

The article describes GameChanger's data pipeline and warehouse, where engineers configure producers, pipeline connections, and warehouse tables. A refactoring project simplified producers, removed boilerplate, and added tests, but creating or updating warehouse tables remained a difficult manual task because the warehouse uses different SQL syntax and lacks library-generated code.

### Source excerpt

As GameChanger's data engineer, I oversee the data pipeline and data warehouse. Sounds simple, right? And at a high level, it is! Fig 1.1: high level architecture diagram. Some complexity removed due to it being kinda boring for this post. Producers produce into the pipeline, and our main consumer is the ETL job which moves data to our warehouse, enabling anybody to come get answers to their questions and see what's happening across all our systems. Boom: easy. Well, not quite. Who owns what Since data can come from any number of backend systems and teams, engineers are responsible for writing the setup that shepherds their data through the system: a producer that lives near their data, the pipe it travels through in the pipeline, and the warehouse table. This often means new data that I'm unfamiliar with arrives in our warehouse without me even knowing it's been set up, which is actually kind of neat: the system should be so easy to work with that you don't need the data engineer. After a recent refactoring project, producers were made as simple as possible with removed boilerplate and plenty of tests to automatically catch the most common bugs engineers encounter. Typically, engineers have no problems with making their producers. Fig 2.1: engineers before and after producer refactor project. Studies have shown that engineers prefer to be happy. The pipe their data travels through is set up by filling in a form and pressing a button. Again, engineers typically have no problems with this. It's the warehouse table that becomes a pain point. Follow the readme There are two times non data engineers need to interact with warehouse tables: they've created a new producer which needs a table for their data to land in. they've updated an existing producer which needs its table updated as well. The second point is trickier and easier to get wrong, but the first point proved just as difficult for many engineers and far more common, especially if the engineers in question had

## 90 Days of Kotlin

DevFeed: [90 Days of Kotlin](<https://devfeed.tech/articles/90-days-of-kotlin-19821.md>)

Original publisher: [Read original article](<https://tech.gc.com/90-days-of-kotlin/>)

Author: GameChanger

Published: 2019-07-10T17:13:53Z

Content type: article

Language: en

Sources: [GameChanger](<https://devfeed.tech/sources/gamechanger.md>)

Topics: [Kotlin](<https://devfeed.tech/topics/kotlin.md>), [Java](<https://devfeed.tech/topics/java.md>), [interoperability](<https://devfeed.tech/topics/interoperability.md>), [Android](<https://devfeed.tech/topics/android.md>), [Compiler](<https://devfeed.tech/topics/compiler.md>), [Programming](<https://devfeed.tech/topics/programming.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [compiler](<https://devfeed.tech/tags/compiler.md>), [interoperability](<https://devfeed.tech/tags/interoperability.md>), [java](<https://devfeed.tech/tags/java.md>), [jvm](<https://devfeed.tech/tags/jvm.md>), [kotlin](<https://devfeed.tech/tags/kotlin.md>), [programming](<https://devfeed.tech/tags/programming.md>)

### AI overview

A developer reflects on 90 days of using Kotlin after years of Android development with Java. The article presents Kotlin as a practical evolution within the JVM ecosystem, emphasizing its interoperability with Java, reduced boilerplate, compiler-assisted features such as type inference and smart casts, and standard-library conveniences.

### Source excerpt

Riding the wave of change Old habits die hard. We're creatures of habit, and provided there's no stimuli that causes us to change and evolve, we'll all very likely stay static. After all the more you repeat something, the better you get at it, and there's little to be gained from change for change's sake save for a new perspective, which can be rather varied in its returns. I've been an Android Developer for a little over 5 years now, and I love Java. Quite a bit actually; its APIs for common data structures, its explicitness and the way it makes it easy to read someone else's code, its OOO approach and how it lends to easy encapsulation and delegation to class instances, its recent adoption of a pseudo functional paradigm with functional interfaces and single abstract methods... the list goes on. Not only does it offer all this, but it does so while being backwards compatible with previous versions of the language. It's therefore not surprising Java and the JVM form the thriving ecosystem they are today. I've spent a considerable amount of time learning about the language and its design decisions and they all seem extremely justifiable to me. However, the programming world moves fast, and with Google embracing Kotlin more and more each year, I needed not only to switch, but to be in an environment that I could use Kotlin in, day in and out. Here at Gamechanger, I've found that, and have been learning and using Kotlin for the past 90 days, and there's quite a lot to like. Fundamentally Kotlin doesn't try to re-invent the wheel, nor is it some great departure from Java that would cause an epic schism or anything of the sort in the JVM ecosystem. If anything, it readily embraces its heritage with 100% interoperability with Java. What Kotlin seeks to do instead, is get out of your way, and let you do as much as you already could with a lot less effort and code. Compiler niceties A lot of what Kotlin does I've found, is to take things the compiler could do, and simply mak

## Fishing For Correlations

DevFeed: [Fishing For Correlations](<https://devfeed.tech/articles/fishing-for-correlations-19823.md>)

Original publisher: [Read original article](<https://tech.gc.com/application-performance-monitoring/>)

Author: GameChanger

Published: 2019-02-12T19:51:49Z

Content type: article

Language: en

Sources: [GameChanger](<https://devfeed.tech/sources/gamechanger.md>)

Topics: [observability](<https://devfeed.tech/topics/observability.md>), [Application Performance Management (APM)](<https://devfeed.tech/topics/apm.md>), [Instrumentation](<https://devfeed.tech/topics/instrumentation.md>), [Koa](<https://devfeed.tech/topics/koa.md>), [Monitoring](<https://devfeed.tech/topics/monitoring.md>), [Node.js](<https://devfeed.tech/topics/node-js.md>), [PostgreSQL](<https://devfeed.tech/topics/postgresql.md>), [Logging](<https://devfeed.tech/topics/logging.md>), [async/await](<https://devfeed.tech/topics/async-await.md>)

Tags: [back-end](<https://devfeed.tech/tags/back-end.md>), [instrumentation](<https://devfeed.tech/tags/instrumentation.md>), [logging](<https://devfeed.tech/tags/logging.md>), [metrics](<https://devfeed.tech/tags/metrics.md>), [monitoring](<https://devfeed.tech/tags/monitoring.md>), [node-js](<https://devfeed.tech/tags/node-js.md>), [observability](<https://devfeed.tech/tags/observability.md>), [performance-monitoring](<https://devfeed.tech/tags/performance-monitoring.md>), [postgresql](<https://devfeed.tech/tags/postgresql.md>)

### AI overview

The article describes GameChanger's effort to improve observability during performance degradation in a new back-end system. It discusses instrumenting a Node.js and Koa application, using PostgreSQL, Datadog metrics and APM, and Loggly for centralized logging. The team can monitor system and request metrics but still faces challenges correlating logs, timelines, and behavioral trends.

### Source excerpt

Have you ever participated in a firefight where the root cause seems unclear, then suddenly the symptoms self-resolve? Sometimes inadvertent action may cause it, other times it appears miraculous. If you find yourself in this situation, or excusing recovery with "it was a one-off blip," your system likely lacks sufficient observability. With development and operations increasingly converging, application instrumentation continues to concern many teams building new platforms and maintaining legacy ones. The general rule of thumb when introducing observability into a system is to instrument everything. However, this philosophy creates a user experience challenge. How do we deliver the salient system correlations to the people who can best leverage them? The platform team at GameChanger recently had the opportunity to tackle this question when we encountered performance degradation while scaling a new back-end system. We are not yet finished, but we have progressed meaningfully. Diving into the black box Modern Node.js, using async/await with Koa, powers GameChanger's nascent Team Manager product. PostgreSQL serves as the primary datastore and PgBouncer manages connection pooling to the database. We already use Datadog to monitor many of our legacy systems. Starting with Datadog for a new system made sense for a number of reasons: Vendor familiarity: engineers already understand the operational model and where to look for diagnostics Breadth of functionality: support for a wide variety of metrics, application performance monitoring (APM) and centralized logging, and mature monitor types, ensure our telemetry can scale alongside the codebase Low adoption cost: integrating new services with our existing Datadog configuration is trivial After building an MVP, our baseline instrumentation included typical system level metrics, such as load average, CPU utilization, and used memory, as well as custom metrics for all requests like duration and status code. Such metrics enabl

## Our Part-Time Testers

DevFeed: [Our Part-Time Testers](<https://devfeed.tech/articles/our-part-time-testers-19841.md>)

Original publisher: [Read original article](<https://tech.gc.com/outfielders/>)

Author: GameChanger

Published: 2018-06-28T18:55:20Z

Content type: article

Language: en

Sources: [GameChanger](<https://devfeed.tech/sources/gamechanger.md>)

Topics: [Testing](<https://devfeed.tech/topics/testing.md>), [Usability](<https://devfeed.tech/topics/usability.md>), [ui](<https://devfeed.tech/topics/ui.md>)

Tags: [bugs](<https://devfeed.tech/tags/bugs.md>), [releases](<https://devfeed.tech/tags/releases.md>), [testing](<https://devfeed.tech/tags/testing.md>), [tests](<https://devfeed.tech/tags/tests.md>), [ui](<https://devfeed.tech/tags/ui.md>), [usability](<https://devfeed.tech/tags/usability.md>)

### AI overview

GameChanger's QA team supplemented automated testing with part-time testers who were avid users of its apps and had some QA experience. Compared with randomly assigned outsourced testers, these users understood the products better, worked faster, found release-blocking and complex bugs, and provided real-world feedback.

### Source excerpt

Up until the latter half of last year, we, the QA team at GameChanger, dedicated many hours to manually running regression tests on our apps. We found ourselves caught in a time crunch before weekly releases, falling behind in testing new features, and struggling to maintain a high standard of quality for our products. An obvious solution would have been to create a suite of automated tests so that we could forego manual regression testing. Although we are starting to work on this as part of our complete test strategy, we still have a need for manual testing for many reasons. For instance, automated tests may not pick up on certain types of UI bugs like a flashing screen and they certainly cannot provide usability feedback. So we came up with the idea of hiring manual testers for regression testing... At first, we hired a company that supplied us with on-demand testers who were randomly assigned to us when we needed to run our regression test suites. This meant that tests were always executed by someone who had never seen our apps before. However, without a deep understanding of functionalities within our apps, these testers were unable to effectively test and find bugs. This solution was a swing-and-a-miss. Assembling the Ultimate Team of Part-Time Testers We needed our QA team to focus on creating test plans, testing new features, and strategizing and implementing ways to attain and maintain higher standards of quality. So we set out to hire our own team of part-time testers to run our daily regressions and beta regressions. When we first thought about hiring testers, we were constantly weighing the importance of hiring someone with solid QA experience versus someone who was very familiar with our apps. At the time, we were so impressed with the enthusiasm of GameChanger users applying for the role, we ended up hiring avid GameChanger users with at least some QA experience. Contrary to the outsourced testers, our newly hired testers were real users who were personal

## Learning Kotlin Constructor as a Java Developer

DevFeed: [Learning Kotlin Constructor as a Java Developer](<https://devfeed.tech/articles/learning-kotlin-constructor-as-a-java-developer-19837.md>)

Original publisher: [Read original article](<https://tech.gc.com/learning-kotlin-constructor-as-a-java-developer/>)

Author: GameChanger

Published: 2018-03-27T20:18:49Z

Content type: article

Language: en

Sources: [GameChanger](<https://devfeed.tech/sources/gamechanger.md>)

Topics: [Kotlin](<https://devfeed.tech/topics/kotlin.md>), [Java](<https://devfeed.tech/topics/java.md>), [Learning](<https://devfeed.tech/topics/learning.md>)

Tags: [article](<https://devfeed.tech/tags/article.md>), [developer](<https://devfeed.tech/tags/developer.md>), [java](<https://devfeed.tech/tags/java.md>), [kotlin](<https://devfeed.tech/tags/kotlin.md>), [learning](<https://devfeed.tech/tags/learning.md>)

### AI overview

A tutorial for Java developers moving to Kotlin that explains primary and secondary constructors, constructor invocation order, initialization timing, and Kotlin's default-argument approach to overloading. It also demonstrates an explicitly discouraged workaround for imitating Java-style constructor behavior.

### Source excerpt

I have been developing Android apps in Java for years. I recently joined GameChanger and was excited to learn that GameChanger is using Kotlin. I originally thought that moving to Kotlin would be as simple as learning some new syntax, but I discovered that there was more to it. After a brief learning period, I'm up and running working in Kotlin. In this article, I'll save you some of the trial-and-error by introducing some important concepts about constructors for those making the jump from Java to Kotlin. Not All Constructors are Created Equal in Kotlin In Java, all constructors are equal in a sense. public class Person { String name; int age; public Person(Person p) { name = p.name; age = p.age; } public Person(String n, int a) { name = n; age = a; } } Take the above example, Person(Person p) and Person(String name, int age) can be used independently. Of course, you can choose to have one calling another, but it's not required by the language. In Kotlin there is always a primary constructor. Any additional constructors are secondary constructors. The primary constructor is always incorporated into the class header. class Person(n: String, a: Int) { var name: String = n var age: Int = a } The variables name and age are initialized with n and a. In fact, n and a are available anywhere in the class for variable initialization. class Person(n: String, a: Int) { var name: String = n var age: Int = a var nameX2: String = n + n init { println("The age is: " + a) } } Any other constructors would become secondary constructors which are required to call the primary constructor in the very beginning. class Person(n: String, a: Int) { var name: String = n var age: Int = a constructor(p: Person) : this(p.name, p.age) { } } The Order of Creation So, what is the order of processing when calling a secondary constructor? The primary constructor is invoked first, which triggers all the initialization from top to bottom. Then, the body of the secondary constructor is executed. class

## Sync for GC Team Manager

DevFeed: [Sync for GC Team Manager](<https://devfeed.tech/articles/sync-for-gc-team-manager-19843.md>)

Original publisher: [Read original article](<https://tech.gc.com/sync-post-2/>)

Author: GameChanger

Published: 2018-01-25T14:10:50Z

Content type: article

Language: en

Sources: [GameChanger](<https://devfeed.tech/sources/gamechanger.md>)

Topics: [App](<https://devfeed.tech/topics/app.md>), [Messaging](<https://devfeed.tech/topics/messaging.md>), [Back end](<https://devfeed.tech/topics/backend.md>), [Algorithm](<https://devfeed.tech/topics/algorithm.md>), [Database](<https://devfeed.tech/topics/database.md>)

Tags: [app](<https://devfeed.tech/tags/app.md>), [article](<https://devfeed.tech/tags/article.md>), [backend](<https://devfeed.tech/tags/backend.md>), [build](<https://devfeed.tech/tags/build.md>), [database](<https://devfeed.tech/tags/database.md>), [messaging](<https://devfeed.tech/tags/messaging.md>), [sync](<https://devfeed.tech/tags/sync.md>)

### AI overview

This article explains how the GC Team Manager app implemented a synchronization system using backend Pub/Sub, asynchronous polling, and granular updates. It focuses on handling out-of-order messages, converting backend database changes into targeted sync topic updates, and iteratively moving from polling toward direct topic notifications.

### Source excerpt

Introduction In my last article I talked about the sync system for our new GC Team Manager app and the trade-offs we considered in our design process. To reiterate, the high-level structure we settled on was a backend Pub/Sub service, with small granular updates, and we needed to account for the lack of ordering in message delivery. In this article I will cover how we implemented and made this sync system work. I mentioned in the last post that part of the reason we settled on Pub/Sub was because we could use an iterative approach instead of all or nothing. This iteration broke down into two distinct parts, Asynchronous Polling Sync and Pub/Sub Sync. Asynchronous Polling Sync relied on Asynchronous Polling and topic updates. Pub/Sub Sync built device notification on top of Pub/Sub Sync. I will describe each in turn. Asynchronous Polling Sync The goal of Asynchronous Polling Sync was to solve a few distinct problems. First, it focused on building an algorithm that was resilient to out-of-order messages received by the devices. Second, it focused on turning backend database updates into targeted sync topic updates. To simplify the initial build, Asynchronous Polling Sync specifically avoided solving the problem of pushing topic updates to the devices themselves. It did this by allowing the devices to use Asynchronous Polling to get all updates. Switching from Asynchronous Polling to direct topic updates is the provenance of Pub/Sub Sync. I will cover each part of Asynchronous Polling Sync. Asynchronous Polling Sync Algorithm Handling Out of Order Messages When thinking about the out-of-order messaging problem, the goal is to ensure that no matter what order message are received in, all devices will end up in a consistent state. When we were thinking through possible ways to do this in our sync system, we realized that the type of message being sent can help you solve this problem. As a demonstration I will consider three types of updates the backend could send to the

## Sync for GC Team Manager

DevFeed: [Sync for GC Team Manager](<https://devfeed.tech/articles/sync-for-gc-team-manager-19842.md>)

Original publisher: [Read original article](<https://tech.gc.com/sync-post-1/>)

Author: GameChanger

Published: 2018-01-24T20:52:42Z

Content type: article

Language: en

Sources: [GameChanger](<https://devfeed.tech/sources/gamechanger.md>)

Topics: [App](<https://devfeed.tech/topics/app.md>), [Back end](<https://devfeed.tech/topics/backend.md>), [Code](<https://devfeed.tech/topics/code.md>), [Concurrent Programming](<https://devfeed.tech/topics/concurrent-programming.md>)

Tags: [app](<https://devfeed.tech/tags/app.md>), [article](<https://devfeed.tech/tags/article.md>), [backend](<https://devfeed.tech/tags/backend.md>), [data](<https://devfeed.tech/tags/data.md>), [distributed-system](<https://devfeed.tech/tags/distributed-system.md>), [reconciliation](<https://devfeed.tech/tags/reconciliation.md>), [sync](<https://devfeed.tech/tags/sync.md>)

### AI overview

This article introduces the sync system being designed for GameChanger's GC Team Manager app. It defines sync as keeping all devices on a shared, up-to-date view of team data, explains why near-immediate propagation matters, and outlines the distributed-system constraints shaping the architecture.

### Source excerpt

Introduction GameChanger recently released an all new app, GC Team Manager. This wonderful new product helps youth sports communities communicate, coordinate, and organize their team's lives. I have been lucky enough to be on the team that gets to build the backend for this new app. Throughout building out this completely new backend, I have gotten to work on a lot of different pieces of the system. The one I want to dive deeply into today is our sync system. Sync is a pretty generic, buzzwordy programming term. Therefore, it is important for me to clarify what sync means in this context. In this article, sync is the processes of ensuring that all devices in our system have a shared, up to date view of the world. If one device sees the state of Team A one way, all other devices should see the exact same state for Team A. If a device makes a change to Team A, that change is reflected instantaneously across all other devices. Note that this is the ideal version of sync. There are many constraints and challenges that make this hard to achieve but this is the goal I set about building towards. The reason sync needed to work this way in our app is: Users expect it - Users have begun to expect near immediate updates to propagate across their and their friends' devices. Avoids confusion between users - The closer to instantaneously that an app updates itself the less likely it is for two users to find divergences in their data. We want to avoid two coaches on the same team seeing different versions of that team's data. Keeps reconciliation simple - The longer period of time where data can diverge, the more work it can be to reconcile changes into one object. We want to try and limit the potential for divergence and to limit the reconciliation complexity. Having settled on what sync means, the rest of this article attempts to describe the design and architectural decisions and trade offs that went into solving the sync problem for our new Team Management app. There will be

## Beyond the Mean

DevFeed: [Beyond the Mean](<https://devfeed.tech/articles/beyond-the-mean-19824.md>)

Original publisher: [Read original article](<https://tech.gc.com/beyond-the-mean/>)

Author: GameChanger

Published: 2017-08-23T15:33:33Z

Content type: opinion

Language: en

Sources: [GameChanger](<https://devfeed.tech/sources/gamechanger.md>)

Topics: [Google](<https://devfeed.tech/topics/google.md>), [Software](<https://devfeed.tech/topics/software.md>), [Sports](<https://devfeed.tech/topics/sports.md>)

Tags: [diversity](<https://devfeed.tech/tags/diversity.md>), [google](<https://devfeed.tech/tags/google.md>), [inclusion](<https://devfeed.tech/tags/inclusion.md>), [insights](<https://devfeed.tech/tags/insights.md>), [news](<https://devfeed.tech/tags/news.md>), [responses](<https://devfeed.tech/tags/responses.md>), [safety](<https://devfeed.tech/tags/safety.md>), [software-engineer](<https://devfeed.tech/tags/software-engineer.md>), [tech](<https://devfeed.tech/tags/tech.md>), [work](<https://devfeed.tech/tags/work.md>)

### AI overview

A woman software engineer at a sports technology company reflects on the Google Memo and its effects on diversity, inclusion, and psychological safety in the technology industry. The article argues that open discussion is important, but the content and framing of opinions also matter when they reinforce gender stereotypes or make colleagues feel less safe.

### Source excerpt

I work as a software engineer at a sports tech company and I am a woman. These facts about my life are why I continue to be distressed about the news of the Google Memo, written by software engineer James Damore, and its ramifications for my industry. While the headlines may make the memo seem like it was written by a deranged maniac, the fact that it wasn't written as a blatant anti-diversity statement (it begins: "I value diversity and inclusion, am not denying that sexism exists, and don't endorse using stereotypes") is one of the things that makes it harder for people to be immediately outraged about its contents (and why this discussion is so fascinating and important). As a software engineer, questioning assumptions and raising potentially controversial ideas is part of the job. I agree with the Damore's assertion that "If we can't have an honest discussion about this, then we can never truly solve the problem." The topic of diversity is critically important and I want to add my voice and insights to the discussion as honestly as possible. I agree with the myriad of responses suggesting that there were many productive ways that Damore could have raised some of his concerns, such as his views that conservative viewpoints aren't valued in tech or that the diversity programs at Google should be more inclusive, without perpetuating gender stereotypes to make his point. I am pained to see that people are now afraid to raise controversial or minority opinions or ask questions because of the consequences of this memo. But the issue for me is not the theoretical or hypothetical raising of unpopular opinions. In theory I absolutely agree that is very important. But I want to focus on the reality of this situation and the memo that was actually published. Damore's main argument is that "Google's political bias has equated the freedom from offense with psychological safety, but shaming into silence is the antithesis of psychological safety." However, you can't ask for ps

## You Don't Need To Guess Right

DevFeed: [You Don't Need To Guess Right](<https://devfeed.tech/articles/you-don-t-need-to-guess-right-19845.md>)

Original publisher: [Read original article](<https://tech.gc.com/you-dont-need-to-guess-right/>)

Author: GameChanger

Published: 2017-05-06T21:07:20Z

Content type: article

Language: en

Sources: [GameChanger](<https://devfeed.tech/sources/gamechanger.md>)

Topics: [Back end](<https://devfeed.tech/topics/backend.md>), [API](<https://devfeed.tech/topics/api.md>), [Framework](<https://devfeed.tech/topics/framework.md>), [Koa](<https://devfeed.tech/topics/koa.md>), [TypeScript](<https://devfeed.tech/topics/typescript.md>), [Database](<https://devfeed.tech/topics/database.md>), [App](<https://devfeed.tech/topics/app.md>)

Tags: [api](<https://devfeed.tech/tags/api.md>), [app](<https://devfeed.tech/tags/app.md>), [backend](<https://devfeed.tech/tags/backend.md>), [building](<https://devfeed.tech/tags/building.md>), [database](<https://devfeed.tech/tags/database.md>), [develop](<https://devfeed.tech/tags/develop.md>), [framework](<https://devfeed.tech/tags/framework.md>), [getting-started](<https://devfeed.tech/tags/getting-started.md>), [modular](<https://devfeed.tech/tags/modular.md>), [project](<https://devfeed.tech/tags/project.md>), [research](<https://devfeed.tech/tags/research.md>), [typescript](<https://devfeed.tech/tags/typescript.md>)

### AI overview

The article describes how two developers approached building a new backend API and app under significant uncertainty. They used modular design and short, well-researched decisions to reduce stress, ultimately choosing TypeScript, Koa, and Postgres for the initial stack.

### Source excerpt

I've been working on a new project recently, and when I say new, I mean brand new. This isn't just a lateral shift on the team to get my hands dirty in a new area of focus. Nor is it even adding a new product to our existing stack. This is building a brand new backend API for a brand new app. I'm really excited about being able to develop a new codebase, but the sheer number of unknowns and amount of work ahead is daunting. I wanted to share how my teammate Alex Etling and I embraced modular design to make our decision making process faster and less stressful. Let's start this story a few weeks ago, at the inception of the new team. The purpose of the app was clear, but I'm not sure I needed two hands to count the number of decisions that had been made. In that setting, Alex and I were given the mandate to figure out what the stack looks like and start building. We are both veterans at GameChanger, but neither of us had taken on something with this combination of future scale and current uncertainty. We've both architected solutions to a hard problems before, but when looking at a blank text editor and wondering what database should we use?, what language should we write this in?, and most terrifyingly what if we make the wrong choice?, it was hard to figure out where to start. Alex and I were getting started a few weeks ahead of the team that was going to be building the client app that will use this API. That means that we didn't have to spin something up immediately, but we knew that in a few weeks we had to have decisions made and a basic API ready for consumption. Everyone else who was working on the project at this point were focused on high level research, not implementation details. Alex and I had to start implementing crucial parts of the stack that could have long lasting consequences if we chose the wrong solution, without the clear definition of future product requirements and direction. It was a bit stressful. So we started. Alex and I split up and did

## Demystifying iOS Layout

DevFeed: [Demystifying iOS Layout](<https://devfeed.tech/articles/demystifying-ios-layout-19829.md>)

Original publisher: [Read original article](<https://tech.gc.com/demystifying-ios-layout/>)

Author: GameChanger

Published: 2017-04-23T02:34:57Z

Content type: article

Language: en

Sources: [GameChanger](<https://devfeed.tech/sources/gamechanger.md>)

Topics: [iOS](<https://devfeed.tech/topics/ios.md>), [App](<https://devfeed.tech/topics/app.md>), [debug](<https://devfeed.tech/topics/debug.md>), [ui](<https://devfeed.tech/topics/ui.md>)

Tags: [app](<https://devfeed.tech/tags/app.md>), [applications](<https://devfeed.tech/tags/applications.md>), [blog-post](<https://devfeed.tech/tags/blog-post.md>), [debug](<https://devfeed.tech/tags/debug.md>), [ios](<https://devfeed.tech/tags/ios.md>), [ui](<https://devfeed.tech/tags/ui.md>)

### AI overview

This article explains how iOS view layout and content updates relate to the application's main run loop. It describes event handling, the update cycle, and the timing of layout, display, and constraint changes, helping developers understand and debug view-update behavior.

### Source excerpt

Some of the most difficult issues to avoid or debug when you first start building iOS applications are those dealing with view layout and content. Often, these issues happen because of misconceptions about when view updates actually occur. Understanding how and when a view updates requires a deeper understanding of the main run loop of an iOS application and how it relates to some of the methods provided by UIView. This blog post will explain these interactions, hopefully clarifying how to use use UIView's methods to get the behavior you want. Main run loop of an iOS app The main run loop of an iOS application is what handles all user input events and triggers the appropriate responses in your application. Any user interaction with the application is added to an event queue. The application object, shown in the diagram below, takes events off the queue and dispatches them to the other objects in the application. It essentially executes the run loop by interpreting input events from the user and calling the corresponding handlers for that input in the application's core objects. These handlers call code written by application developers. Once these method calls return, control returns to the main run loop and the update cycle begins. The update cycle is responsible for laying out and redrawing views (described in the next section). Below is an illustration of how the application communicates with the device and processes user input. https://developer.apple.com/library/content/documentation/General/Conceptual/Devpedia-CocoaApp/MainEventLoop.html Update cycle The update cycle is the point at which control returns to the main run loop after the app finishes running all your event handling code. It's at this point that the system begins updating layout, display, and constraints. If you request a change in a view while it is processing event handlers, the system will mark the view as needing a redraw. At the next update cycle, the system will execute all changes on these