Posts

Ecto - Using select_merge for flexible aggregates

Image
Problem PostgreSQL is a rock solid database.   I've been using aggregate expressions for a number of features over the past few years in various languages.  They can be used to build powerful features for your platform.  When teamed up with Elixir and Ecto, you get amazing flexibility in your query generation.  Typically the data you want to retrieve via a select is fairly static, or there are only one or two different versions of the select you need.   @ doc """ Returns an aggregation of user activity """ @ spec user_activity_history () :: UserActivityHistory def user_activity_history () do query = from ( u in User , select : %UserActivityHistory { captured_at : fragment ( "now()" ) , hour_1 : filter ( count ( u . id ) , fragment ( "? >= now() - interval '1 hour'" , u . last_active )) , hour_6 : filter ( count ( u . id ) , fragment ( "

Phoenix LiveView: Async Assign Pattern

Image
UPDATE: LiveView has added a built in, more robust way of using this pattern.  Check it out here ! I've been using LiveView for about two years now. It's a great framework that makes snappy and responsive pages. One anti-pattern I see fairly often is loading a lot of data in the initial page render. For the un-initiated, the mount/3 function is called twice.  Once for the initial 'dead' render, and again after the socket is connected.   Many times, for the sake of simple straight forward code, not much is done differently between these two renders. I haven't found any references (I'm sure they exist) on a best practice for managing the following flow: Set sensible, lightweight default values on initial render. Kick off one or more async processes to make longer running function calls. Receive the values in the LiveView, and update the assigns. As always in Elixir, the tools are powerful, and theres many ways to accomplish this. Spawn a linked, or unlinked pro

Custom Kaffy Styling

Image
I recently gave a talk at Empex NYC ( link to talk ) on why you should be writing admin tools from day one .  In my talk, one thing I highly suggest is to make it very obvious what environment you are in when providing a web admin UI for your team.   We've been using Kaffy at SwayDM since the beginning.  At the time, you couldn't easily overwrite the styling.  I maintain a private fork of Kaffy for changes I've made, which included the ability to change the background color. I recently merged the past few minor Kaffy releases back into my fork, and saw that Kaffy now has the ability to provide Extensions which allow you to inject custom html, css and javascript.  I spent a little time making a custom CSS file to change the color of the Kaffy nav, and add a title which notes which environment you are in. For each environment, I have a static file for CSS styling overrides. kaffy-ext-local.css kaffy-ext-staging.css kaffy-ext-production.css defmodule SwayDMWeb.Admin.Kaffy

ELI5: ChatGPT is a Giant Plinko Game

Image
Photo By: sumofus ChatGPT is like a giant Plinko game. Plinko is a game where you put a coin in at the top. The coin falls down the board, hitting pins that influence it on the way down. At the bottom are slots, with various points or prizes. Depending on where you put your coin in at the top, you have different chances of which slot it will land it at the bottom. It's not random, some slots will be more likely than others depending on where you start. Now, rather than a coin going in at the top, assume it's your question to ChatGPT. At the bottom, rather than prizes, assume it's all possible words. ChatGPT looks at your question, and determines based on the words you used where to put your question in at the top. Then, the pins influence your question on the "way down" depending on what the words are. Rather than landing in on a single word, ChatGPT outputs a list of the most likely words to come next. ChatGPT then picks a word from the most likely words and show

Write Admin Tools From Day One

Image
UPDATE:  I gave a talk expanding on this blog post at EMPEX NYC 2023 . The Problem Writing useful features for your users is key to a successful product.  It makes sense then that you should maximize your time writing features for those users.   This approach is very effective at the beginning of a product's life, but over time, you may find you are spending more time maintaining the product that developing it. Some of the biggest development time sucks can be: Tracking down the cause of unexpected behavior. Fixing data. Answering user, or team member questions. Running maintenance scripts, or other ad-hoc tasks. The "Oh #&@$" Solution These small support tasks start to add up, and there is no easy way to offload the work to product, or support.  You and your development team reach a breaking point where you must start prioritizing administrative tools. Unfortunately, you will slow down your new feature development even more in the short term.  It will take time for t

Your Software Should Suck

Good software is really hard to write.  When software is good, you almost don't notice it.  The UI / UX doesn't get in your way.  You can do what you want easily. When software is bad, it's frustrating, but you still use it because the alternative is worse.  Maybe it's replacing a manual / tedious process, or provides functionality that simply could not exist otherwise. People will put up with bad software. I've worked on projects where the #1 user requested feature wasn't implemented for over 7 years.   Guess what.  The software still sold, and the company was still successful.  This is because it was better than the alternative. So what makes good software?    Good software doesn't start good.  It most likely sucked.  The odds that you will get every aspect of your software correct on your first iteration are slim.    So don't try to pack every feature into that first release.  Get the bare minimum out there, and get feedback.  The key is to listen to

Fingerprinting Knock errors for Sentry in Elixir

Image
At SwayDM , we use the fantastic Knock platform to define notification workflows, and deliver emails, and SMSs to our users. Our codebase is written in Elixir , and we use Sentry for error reporting via sentry-elixir .  One thing you can do with sentry-elixir is report an event to Sentry every time your app logs at the error level.  You can also specify which metadata logger keys are forwarded to sentry as metadata attributes. This works great in conjunction with Knock, but if you only log the error, all your Knock related issues in Sentry will be grouped as one, regardless of which identify, or workflow caused them.   This is where custom fingerprinting comes in. Configure Sentry First, configure sentry to define a module, and function to be invoked before the event is sent to Sentry. You must have the Sentry LoggerBackend configured.  If you want to pass logger metadata to sentry, you must explicitly add it to this configuration. Capture Logger Metadata Capture details in the logge