<![CDATA[ef4's Dev Blog]]>https://eaf4.com/https://eaf4.com/favicon.pngef4's Dev Bloghttps://eaf4.com/Ghost 6.22Fri, 13 Mar 2026 22:46:49 GMT60<![CDATA[Fun Fast Tools for Serious Work]]>This post serves as a permalink to accompany my talk from EmberConf 2021.

Mho

The demo of the service-worker-based build tool is available here with instructions to run both the prebuilt binary (currently compiled only for OSX, sorry!) as well as how to rebuild everything yourself.

It's demo

]]>
https://eaf4.com/emberconf2021/6063ad28607116003ba20f50Tue, 30 Mar 2021 23:01:57 GMT

This post serves as a permalink to accompany my talk from EmberConf 2021.

Mho

The demo of the service-worker-based build tool is available here with instructions to run both the prebuilt binary (currently compiled only for OSX, sorry!) as well as how to rebuild everything yourself.

It's demo quality. There are pieces of it that are still scaffolded. I implemented enough features of the Embroider v2 spec to get realistic results, proving that we can, for example, crawl all the addon metadata and combine their public assets. I didn't implement the full spec yet, so a few things are handled in app-specific ways. I do think the results are representative, and I intend to keep developing mho until it's a production-quality build tool.

Standalone Webpack

To run an Ember app with standalone webpack, here's a sketch of what your webpack config needs to look like. Thanks to Kris Selden for sharing his: https://gist.github.com/krisselden/7a988d65a7481dac1450dc3ae934f692

]]>
<![CDATA[Dynamic import() into your Ember app]]>https://eaf4.com/dynamic-import-into-your-ember-app/5b48bb786fb23b00bf6c643bFri, 13 Jul 2018 15:04:09 GMTDynamic import() into your Ember app

As of yesterday, ember-auto-import can use ECMA dynamic import() to lazily load any dependency into your Ember app (or addon).

To demonstrate how it works, here's a screencast where I build an app that fetches some real data from my city and plots it using Highcharts. The code for Highcharts is only loaded when needed, using dynamic import().

The demo app's source is available here.

Photo credit unsplash-logoGuillaume Bolduc

]]>
<![CDATA[Ember 2018 Priorities]]>https://eaf4.com/ember-2018-roadmap/5aff7b44da594e00bf2bd672Sat, 19 May 2018 02:26:19 GMTEmber 2018 Priorities

This is my contribution to the Ember 2018 Call for Posts.

When it comes to community goals for this year, I want to highlight two major themes:

  1. Taking all our great ongoing work over the finish line.
  2. Letting our newer contributors shine and lead.

Stick the Landing

Our first priority should be to finish shipping the hugely valuable features that our community has been building for the past twelve to eighteen months. It's a really big list! And the bulk of it is getting so close to done. I'm including things like:

Many of these things can already be tried out today, because the implementations are nearing completion. If we keep executing, a year from now we will take all these features for granted, and that will be wonderful.

Let our newer contributors shine and lead

There are plenty of folks like me who have been part of the Ember community for six years. That is like two centuries in Javascript years. We have a lot of accumulated experience and that is a key part of why we've been able to build durable software that continues to evolve while bringing even the biggest, most ambitious apps along.

But we also have an energetic crop of newer contributors who have been absolutely killing it lately. I don't want to try to name names because I will miss some people, because there is more activity than I can track. They are a big reason Ember's future looks very bright to me.

Our priority as a community should be making sure someone who has been contributing great work for half a year feels as much sense of ownership as someone who has been around since Sproutcore 1.6rc2. Our newer contributors are also some of the best ambassadors for teaching and spreading Ember, because they have the zeal of the newly converted, and they don't have as much Curse of Knowledge as some of us Elder programmers.

So if you have been contributing -- for a few months or a few years, through code, docs, events, answering questions, outreach, or the addon ecosystem -- and you ever feel like you're stymied and don't know how to make things happen, please get in touch. If you're thinking questions like:

  • how can I lead people to consensus on this new feature?
  • how can I help implement this new things everybody agrees they want but nobody has built yet?
  • am I really ready to give a big conference talk?
  • how can we make the process for contributing to the docs / the guides / the website / the addon ecosystem more open and accessible?

this is an invitation to chat about it. I want to help you succeed and I urge everyone else who is perceived as an oldie with community authority to do the same.


Photo credit unsplash-logorawpixel

]]>
<![CDATA[Living Animation]]>

What ambitious web application developers need to learn from game developers. Presented at EmberConf 2018 in Portland.

]]>
https://eaf4.com/living-animation/5af13d0a3dcfb3002261a8b9Tue, 08 May 2018 06:01:55 GMT

What ambitious web application developers need to learn from game developers. Presented at EmberConf 2018 in Portland.

]]>
<![CDATA[Design for Learning]]>

This is a wide-ranging talk on learning and why you need to understand how people learn if you want to build good software. Plus I build a fun orbital mechanics demo. Presented at EmberNYC, March 2018.

]]>
https://eaf4.com/design-for-learning/5af13c323dcfb3002261a8b6Tue, 08 May 2018 05:58:47 GMT

This is a wide-ranging talk on learning and why you need to understand how people learn if you want to build good software. Plus I build a fun orbital mechanics demo. Presented at EmberNYC, March 2018.

]]>
<![CDATA[How to Add TailwindCSS to an Ember App]]>

There is definitely an intimidation factor some people feel about integrating things into Ember apps. While there are plenty of things I want to make easier too, I think a large amount of the perception only comes from lack of good learning materials.

Toward that goal, I made this screencast

]]>
https://eaf4.com/how-to-add-tailwindcss-to-an-ember-app/5aed26513dcfb3002261a8adSat, 05 May 2018 03:39:25 GMT

There is definitely an intimidation factor some people feel about integrating things into Ember apps. While there are plenty of things I want to make easier too, I think a large amount of the perception only comes from lack of good learning materials.

Toward that goal, I made this screencast using TailwindCSS as an example, since it was recently brought up as an example of something that people think would be very hard to integrate.

The accompanying code is on GitHub. If you have comments or questions please chime in on the Ember forums.

]]>
<![CDATA[Mind and Hand]]>

"Mens et Manus" (latin for "Mind and Hand") is the official motto of MIT. Lots of schools have Latin mottoes, and they are usually pretty bland. But mens et manus was — and still is — a radical social statement.

It says that knowing and doing

]]>
https://eaf4.com/mind-and-hand/5a846a12f911ac0018cfb974Mon, 18 Sep 2017 15:38:02 GMT

"Mens et Manus" (latin for "Mind and Hand") is the official motto of MIT. Lots of schools have Latin mottoes, and they are usually pretty bland. But mens et manus was — and still is — a radical social statement.

It says that knowing and doing are equally important. It's radical because the traditional academy and aristocratic class believed that doing was lesser work for lesser people. They were concerned only with knowing. "Veritas".

Seal of MIT

MIT is a world-leading place today because it turns out you can't really be great at knowing without also doing. You can't do cutting edge science without doing cutting-edge engineering to make new tools and gather better data. The closer you look, the fuzzier the distinction gets. "Mens et manus" is a statement that you aren't fully-educated until you appreciate both sides of the coin.

There is still plenty of social resistance to mens et manus. Many organizations are structured as if one caste of leaders should do all the thinking, and another caste of workers should do all the doing.

Nowhere is that attitude more self-defeating than in software. Because software is one of the purest embodiments of the mens et manus duality: the act of knowing precisely what the software needs to do is identical to the act of making the software. But you can't know precisely what it needs to do until you try to make it.

In Memoriam

I was inspired to write this post today because I heard about the passing of Paul Gray. Professor Gray was an exemplar of mens et manus because despite rising to the highest leadership positions in the Institute, he still returned to doing the hard work of teaching big introductory courses to undergrads. And he did that work with immense energy and care for students.

]]>
<![CDATA[Effective team culture: Five Whys & Stop-the-line]]>

When Google set out to measure what makes their best software development teams successful, they found a strong correlation with psychological safety:

A shared belief held by members of a team that the team is safe for interpersonal risk-taking... A sense of confidence that the team will not embarrass, reject

]]>
https://eaf4.com/five-whys/5a846a12f911ac0018cfb973Mon, 28 Aug 2017 19:29:30 GMTEffective team culture: Five Whys & Stop-the-line

When Google set out to measure what makes their best software development teams successful, they found a strong correlation with psychological safety:

A shared belief held by members of a team that the team is safe for interpersonal risk-taking... A sense of confidence that the team will not embarrass, reject or punish someone for speaking up.

I find it interesting that this lends explanatory power to two much earlier ideas that came out of the famous Toyota Production System: "Five Whys" and "Stop-the-line". Both of them are directly relevant to building a software team with more psychological safety.

Five Whys

When a problem happens, it's tempting to accept the first explanation.

Q. Why is our site down!?

A. Because Ed accidentally deleted the production database!

The idea of Five Whys is to force yourself to keep asking questions until you uncover deeper causes.

Q2. Why did he delete it?

A. Because he typed the wrong command.

Q3. Why was he typing that command at all?

A. He was trying to re-deploy the staging environment, and accidentally hit production instead. He was typing commands because our deploy process is manual.

Q4. Why is it manual?

A. Because we're under-prioritizing site-reliability work. Let's use this incident to explain to the business why we need to spend time on automated deployment.

Q5. Why was he using credentials sufficient to delete the production database?

A. Because we use the same credentials in staging and production. I guess we should change that.

Five Whys has a direct benefit (finding and fixing deeper problems). But it has an even more powerful indirect benefit of improving your team's psychological safety. Mistakes are treated as gaps in process, technology, or training. Not as personal failings.

Stop-the-line

Another widely-admired part of the Toyota Production System is the rule that any person on the assembly line is obligated to push the Big Red Button that stops the entire assembly line if they see anything wrong. And the whole team will gather to identify the root cause before starting things up again.

When it was first introduced, this ran directly counter to dogma. Stopping the line is incredibly expensive, you're supposed to do everything you can to keep it running.

But iterative process improvements are like compound interest — over time they make an overwhelming difference that more than pays for stopped assembly lines (or paused feature development work).

It is your obligation to push the button. You never get punished for it, and you never get told you shouldn't have. Even if you turn out to be wrong, the fact that you perceived a problem where there wasn't one was itself a problem, pointing to the need for better design or better training.

In both software and manufacturing, stop-the-line leads to improvements that are otherwise hard to make because it isolates specific problems precisely when they are occurring. There is no better time to fix the underlying cause.

In software, stop-the-line doesn't involve pausing an assembly line. Instead, it means pausing work on your currently assigned feature in order to solve another problem that you hit along the way. If you hit the bug, others are likely to hit it too. So it's worth fixing, or at least worth talking to the rest of the team about it.

A culture of stop-the-line directly fosters psychological safety. Everyone — even the most junior members of the team — should feel safe pointing out problems, without fearing that they might be ridiculed for being wrong or accused of criticizing their peers.

Necessary but not sufficient

There's more to psychologically safe teams than just these two principles. But I think they're worth talking about because they're relatively easy to teach and understand, and they may catalyze introspection that uncovers other cultural/interpersonal problems.

They're also a litmus test for a junior team member who's wondering "am I bad at this, or is this a toxic environment?". If your organization can't or won't honestly engage in this kind of self-examination, it's not you, it's them.

Image credit Creative Common by-nc-nd Toyota UK

]]>
<![CDATA[Programming doesn't fit in your brain]]>

That quote is true, and it's a specific example of a more general principal:

]]>
https://eaf4.com/programming-doesnt-fit-in-your-brain/5a846a12f911ac0018cfb971Wed, 31 May 2017 16:20:37 GMT

That quote is true, and it's a specific example of a more general principal: get comfortable with not being able to fit everything in your head simultaneously.

Beginners often try to hold whole programs or whole problems in their head. And they start to unconsciously believe that getting better at programming involves being able to hold bigger programs and bigger problems in their heads.

This is a trap.

I suspect that people with incredible working memory are actually handicapped at learning programming, because they can get through most practice problems without ever learning the critical skill of paging things in and out of your mind.

You literally need to be OK with forgetting things in order to free up space, and trust that you'll have a good system for relearning what you forgot next time you need it. Sometimes that involves Googling, sometimes it's a well-crafted search through your code or documentation.

This ability to retrieve information on demand is the underlying motivation for all the things we do to manage complexity. Well-structured code allows you to forget about most of it most of the time, and learn or re-learn individual pieces on demand.

Photo credit mimitalks via Flickr, CC by-nc-nd.

]]>
<![CDATA[Search-first Web Applications]]>

Lots of software projects struggle when they try to implement search-related features. I was reminded of this by @trek's reaction to this Algolia marketing tweet:

]]>
https://eaf4.com/search-first-web-apps/5a846a12f911ac0018cfb970Thu, 11 May 2017 14:47:05 GMTSearch-first Web Applications

Lots of software projects struggle when they try to implement search-related features. I was reminded of this by @trek's reaction to this Algolia marketing tweet:

I have heard nothing but good things about Algolia's product, and if you're one of those teams that has been putting off the "Add Search" item on your TODO list, outsourcing it to them is probably not a bad idea.

But instead of treating search as something you can tack on later, what if you make search the foundational primitive for your whole application? That is the approach we're taking in Cardstack.

In a trivial sense, every screen of your application is already driven by "search". An SQL query, a GraphQL query, a JSONAPI GET: these are all specialized forms of search. But when the requirements change and you need to tune any of them for relevance, personalization, fuzzy matches, relatedness, or additional heterogeneous types, you're left painstakingly hand-coding new behaviors. These searches are brittle because the data access patterns and the resulting UI patterns are all hard-coded directly into the application.

In contrast, a search-first application:

  1. Handles every data fetch via a search engine: a system that is good at both structured and fuzzy queries, optimized for low-latency. (Examples include Elasticsearch and Algolia.)

  2. Allows the search results to drive the resulting UI. Every piece of content returned by the search is expected to have a corresponding UI component that is appropriate for displaying in the current context.

This results in fluid applications that are better at uniting disparate pieces of data into holistic experiences. And it leads us toward the big-picture vision of an open card ecosystem — once the whole experience is driven by search and families of UI components with standardized external APIs, there's no reason all the components need to come from one vendor/application/author. The user can remix micro-applications together into more than the sum of their parts.

]]>
<![CDATA[CSS and Markup in Javascript is an Evolutionary Dead End]]>

There is a popular trend among some Javascript developers to argue that humans should stop writing CSS and HTML (and HTML-based templates). They argue everything should instead be written as Javascript, to reduce complexity and present a more unified developer experience.

And certainly many people who try this approach like

]]>
https://eaf4.com/evolutionary-dead-end/5a846a12f911ac0018cfb96fThu, 04 May 2017 16:40:13 GMTCSS and Markup in Javascript is an Evolutionary Dead End

There is a popular trend among some Javascript developers to argue that humans should stop writing CSS and HTML (and HTML-based templates). They argue everything should instead be written as Javascript, to reduce complexity and present a more unified developer experience.

And certainly many people who try this approach like it. It works well for a certain kind of team. But this approach over-optimizes for the present, and discounts the future. It's going to get leapfrogged by technologies that are already starting to land.

As the web platform matures as an application runtime, it's starting to become a true compilation target.

Q: Sure, almost everybody transpiles something these days, how is this news?

A: There is a critical tipping point that we haven't hit quite yet. A true compilation target is one that you don't need to understand to use.

Today, everyone doing practical work in compile-to-Javscript languages needs to know Javascript too. The tooling simply isn't mature enough and the abstractions leak too much. Even if you write in some other language, you need to think in Javascript to debug anything.

We have seen this all before. There was a time when you couldn't be a professional programmer without knowing C. Your higher-level language was implemented in C, and it would break a lot, and you would need to debug the C layer. Further back, you couldn't be a programmer if you didn't know assembly, because C compilers were too leaky an abstraction, and debuggers were not good enough to debug-while-thinking-in-C, so you had to debug-while-thinking-in-assembly.

But eventually, layers mature and become mostly ignorable. You don't need to know assembly today to be productive in most parts of programming. You don't need to know C. And we are on the cusp of the day when many web programmers won't need to know Javascript.

The reason for this is that both the runtime capabilities (in the form of WebAssembly and ASM.js) and the develop & debug facilities are advancing quickly.

Every time this process has repeated in the past, the old guard who were already experts resisted the change, and denigrated the newcomers who had the gall to call themselves programmers even though they didn't know assembly! But the newcomers win in the end, because there are so many more of them, and the nexus of activity moves up the stack.

It's the nature of software to make last year's cutting-edge problem into next year's moderately-hard problem into something a precocious child can do a few years after that. Realize there are many of us out there working toward the point where a kid in a weekend can reproduce your $X million app by plugging together reusable components. It's not a question of if, but when.

And we're going to succeed because we control all the language semantics and choose how they map into underlying Javscript (or webassembly) semantics. That ability to choose gives us immense leverage over the future.

How does this relate back to whether HTML and CSS belong in Javascript? The bet to make is that we're going to see more use of specialized languages. And HTML and CSS are the grandaddy specialized languages that have enough social consensus and capital investment to be the seeds of the next generation. A project like the Glimmer Virtual Machine is really a new programming language being born, one that has standards-based HTML baked into its DNA.

When we succeed, it will not be because we turned everyone into Javascript ninjas. It will be because we made it radically easier to compose applications. Fully-general programming languages are irreducibly complex. You can only go so far in automatically helping people with them before you summon demons like the halting problem. Specialized languages can put a velvet rope between most users and the deep abyss of computational complexity, and present gradual onramps for users who have the need and desire to begin exploring those nether regions.

If you plant your flag on "it's just Javascript semantics", you don't have that flexibility. You cannot evolve as far or as fast.

The asteroid is coming.

Photo credit: Daniel Mennerich via Visualhunt / CC BY-NC-SA

]]>
<![CDATA[Cardstack Architecture Notes]]>

Many people have expressed an interest in contributing to Cardstack and have been clamoring for some docs to help them get started. This is the first of three posts I intend to ship in the near term to unlock some of that potential energy:

  1. Cardstack Architecture Notes. This post. A
]]>
https://eaf4.com/cardstack-architecture-notes/5a846a12f911ac0018cfb96dWed, 03 May 2017 13:42:33 GMTCardstack Architecture Notes

Many people have expressed an interest in contributing to Cardstack and have been clamoring for some docs to help them get started. This is the first of three posts I intend to ship in the near term to unlock some of that potential energy:

  1. Cardstack Architecture Notes. This post. A high-level overview — breath over depth — that points out many of the key concepts and their motivations.
  2. Roadmap. Where are we headed, in terms of features that don't exist yet but are needed.
  3. Working demo app.

For a more general introduction to Cardstack and the motivations behind the project, you may want to watch this talk I gave at EmberConf:

Orientation: Relationship to Ember & Glimmer

Glimmer is a fast, small UI component rendering engine for the web. It gives us declarative templates that are carefully designed to be a superset of HTML that's appropriate for rich, dynamic applications. Glimmer's internals are fascinating, though not critical to this post. (It has an optimizing compiler and two-phase bytecode virtual machine that's as fast as virtual-dom implementations on initial render while remaining faster than them on update.)

Ember is a batteries-included framework for building ambitious web applications. It is built on top of Glimmer (which was originally extracted from Ember's fourth-generation rendering engine). Ember's greatest strength is its welcoming community that champions shared solutions and stability without stagnation, which has enabled a deep ecosystem of shared code to grow and flourish. Ember is an antidote to "Javascript fatigue".

Cardstack is built on top of Ember, and so in one sense is just a family of Ember Addons. But Cardstack differs from Ember by also offering standardized server-side code. This allows Cardstack plugins to do more out-of-the-box than Ember addons can normally do.

My goal is to bridge two worlds:

  • Cardstack should be useful to people who are already writing Ember apps who want to incrementally adopt its features. They are the early-adopter audience who can get the most out of it today and who can help push the project to maturity.
  • But we are also consciously building toward the point where a new, much bigger audience can adopt Cardstack: organizations that don't have the experience, budget, or appetite for risk that's needed today to build custom in-browser applications. People who would have otherwise chosen from the earlier generation of content management systems, because those systems do more out-of-the-box.

This goal dictates parts of the high-level Cardstack architecture: it's implemented as a family of separate packages, so that it can be incrementally adopted and the individual packages can be immediately useful in the Ember ecosystem. And it's designed with the ultimate goal of letting people ship ambitious applications to production without writing a line of Javascript.

Source Code

The source for all the Cardstack packages lives in the cardstack/cardstack repo. Each package is published to npm separately, under the @cardstack organization.

Several related Ember addons that are not Cardstack-specific have been spun out as completely independent repos, such as ember-toolbars. This follows the general rule: purely client-side concerns fall within the sphere of Ember, and so they ship as plain old Ember addons. Concerns that span client and server are Cardstack plugins, the core set of which lives within the cardstack/cardstack repo.

Hub, Plugins, Features

The main server entry point is the @cardstack/hub package. The Hub is not a framework for writing server apps, it is a server app. You configure and customize the Hub by adding plugins.

Cardstack plugins are npm packages that have "cardstack-plugin" in their keywords list and a cardstack-plugin section in the package.json file. By default, their Cardstack-specific code is located in their top-level directory, but this can be customized by setting the src property in the cardstack-plugin section of package.json (this is important because many Cardstack plugins are also Ember Addons, and it's nice to be able to use a completely stock Ember Addon layout and move the Cardstack-specific bits into a subdirectory).

Each Cardstack plugin can provide one or more Features. Examples of Features include:

  • field types
  • constraints
  • writers
  • indexers
  • searchers
  • authenticators
  • middleware

Running the Hub

To start up, the Hub needs seed models. These contain the minimal set of configuration needed to access the rest of its data. For an example, see the blueprint included in @cardstack/git.

In addition to being a standalone node package, the Hub is also an Ember Addon that registers development-mode and test-mode middleware with ember-cli, so that the Hub runs automatically when you do ember serve or ember test. When run this way, it mounts itself under the URL /cardstack within ember-cli's web server, and it conventionally loads seed models from cardstack/seeds/development.js or cardstack/seeds/test.js.

In production, you can start the Hub server directly via @cardstack/hub's bin/server.js, and pass the location of the production seed models as the only argument.

Don't actually deploy the Hub to production right now, this is pre-alpha software and not all authorization features are fully implemented.

Plugin Loading

Plugins must be activated before they will take effect. Eventually this will be controlled via admin UI, but for right now you do it by adding a plugin-configs model whose module attribute contains the name of the plugin's npm package. For example, assuming you are running the Hub via ember-cli in development:

POST http://localhost:4200/cardstack/plugin-configs
Content-Type: application/vnd.api+json
{
  type: 'plugin-configs',
  attributes: {
    module: '@cardstack/mobiledoc'
  }
}

Data Sources

The Hub is not the authoritative storage location for any of your data. It has no database of its own, at least not in the way you may be accustomed to. What it has is a fast cache & search index, powered internally by Elasticsearch. It uses plugins to index, search, and write to whatever set of upstream data sources you need.

This gives us the ability to present an idiomatic, JSONAPI-compliant, performant API with uniform query semantics, even when the data comes from disparate legacy system, third-party services, or hosted databases of your choice.

Data source plugins are implemented via some combination of

  • an indexer, which ingests upstream data and emits JSONAPI documents for the Hub to cache and serve. A good example is the indexer in @cardstack/git.
  • a writer, which writes changes directly to the upstream data source (after which an indexer can pick them up -- we always go round-trip to avoid split-brain synchronization problems). A good example is the writer in @cardstack/git.
  • a searcher which can do data-source-specific deep searching of data that you don't want to fully index. A good example is the searcher in @cardstack/github-auth, which lets you choose to outsource your user models to GitHub.

Schema

The Hub needs to understand your content's schema in order to properly index it, expose endpoints for reading and writing it, validate it, etc. Within the Hub, content, schema, and configuration are all represented as JSONAPI documents.

Consumers of the Hub's web-facing JSONAPI interface don't really need to make a distinction between what is content vs schema. However, authors of data source plugins need to be aware that schema is defined as models that alter how other models will be indexed. For example, if you add a new field to a content-type model, that alters the schema (and potentially requires some content to be reindexed). Whereas if you create a new article model, that is just a content change.

The list of types that are treated as schema is located here in the source. The @cardstack/hub package also contains the bootstrap schema, without which we would have no way to getting started (what fields are allowed when POSTing a new content-type? The bootstrap schema is where that information comes from.) The bootstrap schema contains built-in types like:

  • content-types You might create content-types like "events" and "articles". Each of these can map to a DS.Model class within ember-data.
  • fields You might create "title" and "body" fields that you will want to attach to your "articles" content type. Each of these can map to DS.attr, DS.belongsTo, or DS.hasMany in ember-data.
  • constraints You might attach a "length less than 40 characters" constraint to your "title" field.
  • default-values You may want to create a "now" default value that you can attach to a "last-edited-date" field.
  • grants You may want to express a rule that users in a particular team may edit or read certain content types or fields.

There is no requirement that the schema for a content-type and the content-type itself are stored in the same data source. You can keep all your shema models in a data source like @cardstack/git, even though some of the types are stored in a different data source. Alternatively, data source plugins are free to emit schema models if they want to -- this allows dynamic discovery of upstream schema.

JSONAPI

The @cardstack/jsonapi plugin contains Hub middleware that exposes endpoints for all your content and schema models. The intent is that you shouldn't need to implement custom middleware just to hold business logic -- business logic can be done via a combination of plugins that provide authenticators, grants, constraints, default values, and writers.

Authentication

The @cardstack/authentication package provides support for clientside sessions (via an ember-simple-auth authenticator) along with a Hub middleware that provides the server-side endpoints for establishing sessions.

Activating @cardstack/authentication opts your app into Cardstack-controlled sessions, but it doesn't implement any particular strategy. For that you also need to activate a plugin that offers an authenticator feature and configure it as an authentication source. An example is @cardstack/github-auth, which uses GitHub OAuth login.

Authentication plugins shouldn't necessarily dictate anything about your user model or where it's stored. The @cardstack/authentication test suite has illustrative examples of the ways you can rewrite upstream users to link them with your own user content-types.

Authorization

Authorization is built into @cardstack/hub, since it tends to be a cross-cutting concern. We have a fairly complete implementation of authentication for all write operations at both the content-type and field level. The upcoming roadmap post will go into more detail on what's needed for read-operation grants and mappings between users and groups.

Clientside Tools

The @cardstack/tools package contains the generic clientside components for rendering and tracking Hub-managed content within an Ember app and making it editable as appropriate.

The cardstack-content component is the entry point for rendering any piece of Cardstack content. It accepts a content model and a format:

{{cardstack-content content=model format="page"}}

It then uses naming conventions to find an appropriate component template to use. In the above example, if the model is of type "article", we would look for a cardstack/article-page component.

Within that component template, you would use the cs-field component to display the fields from the content, either using their default renderers:

{{cs-field content "title"}}

Or by accessing the field's value directly and customizing how it's handled:

{{#cs-field content "avatarURL" as |url|}}
  <img src={{url}} />
{{/cs-field}}

In either case, during editing the presence of cardstack-content and cs-field lets the tools discover which parts of the page belongs to which piece of content and where its fields are rendered.

An upcoming goal is to use a compile-time template transformation to simplify {{cs-field content "foo"}} to {{foo}}, which would work because we can do schema-dependent compilation.

Models

The roadmap post will cover this in more detail, but the general idea is that we want to generate Ember Data models directly from the Hub's schema so you don't need to hand-code them. This is not implemented today, and when experimenting with Cardstack you will generally need to create your own models (and probably adapters and serializers).

Routing

The @cardstack/routing package lets you delegate a subsection of your Ember app's routes to Cardstack's conventions. It provides routes for all content types, pleasant error handling for missing content, and branch query parameter support that lets your preview different versions of your site.

It's fine to not use @cardstack/routing, particularly as you're learning and debugging. It's not especially configurable yet and you may have more luck making your own routes, peeking into @cardstack/routing for ideas. But of course the longer-term goal is to not require people to hand-write routes, so this package will be important.

Field Type Plugins

Field type plugins provide

  • Ember components for rendering and editing
  • server-side functions for validation and search indexing
  • client-side functions that control how to display placeholder content for empty states

The most comprehensive example is @cardstack/mobiledoc. It provides three components. Their names follow Cardstack conventions that allow them to be discovered by @cardstack/tools:

  • field-editors/mobiledoc-editor implements the component that appears within the Cardstack sidebar to represent a mobiledoc field.
  • inline-field-editors/mobiledoc-editor implements the component that is rendered inline on top of your field's content while editing it. This is optional, not every field type needs this.
  • field-renderers/mobiledoc-renderer provides the default rendering output for mobiledoc fields.

It also includes app/fields/mobiledoc.js, which customizes the way empty mobiledoc fields appear.

And it includes a cardstack field feature cardstack/field.js that provides the server-side methods used by the Hub to customize how mobiledoc fields are validating, indexed, and searched.

Search

The general strategy for assembling many pieces of content into a particular page is to provide a @cardstack/search package that provides:

  • components like cardstack-search that accept a query and yield content models
  • a field type implementation for query, with editor components that let end users build and tune search queries.

Branching

Cardstack uses the concept of branches to allow multiple versions of a site to coexist at once. This enables powerful workflows.

While the concept and name "branch" definitely comes from Git, it's important to note that other data sources that don't happen to be Git can also be configured to support multiple branches of your site. For example, a PostgreSQL plugin should allow you to provide different database configurations that correspond to site branches like "dev", "staging", and "production".

The intent is that code, content, and schema can all be changed on one branch and will take effect immediately when viewing that branch's version of the site.

There are necessarily some exceptions, and a given server has a "controlling branch" that is more important than the others. The controlling branch is where the Hub reads configuration that controls authorization decisions, the base set of data sources that are enabled, etc. Attempting to do all these things per-branch gets too difficult to understand and manage.

]]>
<![CDATA[Lake Wobegon Web Development]]>

There is a strain of thought in the web development community that believes there's a tension between having comprehensive developer tools and delivering good user experiences:

This is exactly backwards. At the scale of the entire web, better tools are the only way to deliver better experiences. The

]]>
https://eaf4.com/lake-wobegon-web-development/5a846a12f911ac0018cfb96cFri, 30 Dec 2016 16:23:02 GMT

There is a strain of thought in the web development community that believes there's a tension between having comprehensive developer tools and delivering good user experiences:

This is exactly backwards. At the scale of the entire web, better tools are the only way to deliver better experiences. The reason comes down to statistics.

...that's the news from Lake Wobegon, where all the women are strong, all the men are good looking, and all the children developers are above average.

The average dev team has average skills and knowledge. This will always be true, unless you're in Lake Wobegon. And the explosive growth of the web is drawing an ever-expanding set of people into development. The average experience level is very low and is going to stay low for the foreseeable future.

But all is not lost. Software gives us the power to embed skills and knowledge into tools. Doing this is hard work and it takes time to iteratively improve. But there's an immense payoff when you put world-class, best-practice knowledge into the hands of every person who has something they want to create.

When we get to the happy future day in which the average web application is blazingly fast, standards compliant, accessible, resilient to poor mobile networks, as feature-rich as any native app, and delivered on schedule and under budget, it won't be because the average team got dramatically better. It will be because all those capabilities were packaged into well-crafted tools that empower a broad audience of creators.

This requires more use of comprehensive tools that "want to be there at the start". There's zero probability that we're going to suddenly discover several million new web developers who are all masters of optimizing time to initial paint, balancing server vs client rendering, integrating third party deps, leveraging Service Workers, setting up CI, building test suites, avoiding DOM thrashing, avoiding XSS, not breaking the back button, responsive layout, efficiently fetching additional data, and the thousand other details a great interactive app needs to get right. I guarantee that the future of web development involves tools that make sensible decisions about all those things at the start.

]]>
<![CDATA[Strong Conventions Make Hiring Easier]]>

The most reliable way to evaluate programmer candidates is to hire them to do a bit of realistic work. This is widely understood, but not widely practiced.

The biggest barrier is finding projects for them to work on. In most organizations, the overhead of getting a new person started is

]]>
https://eaf4.com/strong-conventions-make-hiring-easier/5a846a12f911ac0018cfb96bThu, 10 Nov 2016 17:42:28 GMT

The most reliable way to evaluate programmer candidates is to hire them to do a bit of realistic work. This is widely understood, but not widely practiced.

The biggest barrier is finding projects for them to work on. In most organizations, the overhead of getting a new person started is too high to justify handing even the smallest slice of real work off to a candidate. For examples, in "The One Method I’ve Used to Eliminate Bad Tech Hires", Amir Yasin writes:

RULE #2 — DON’T use a real problem because of tribe knowledge needed to fix.

But this rule is missing a key opportunity: there are tribes much bigger than your company. When you share knowledge and conventions across a wider community, a new person can come into your team and instantly be productive. That is how things actually work in the Ember community.

It's entirely normal to hand someone with Ember experience access to a Git repository they've never seen before and instructions to add a small new feature, and to get back working code in hours. Code that actually fits in with "how we do things here". That's the value of having strong conventions.

That's great for hiring intermediate-to-advanced developers, but what about junior-level people? Conventions shine there too, because you can reach for off-the-shelf teaching materials to get them rolling.

The cost of learning & training never disappears, but the key trick here is broadly sharing the cost. Each time you force developers to make a curation decision with multiple valid answers, you fragment the pool of people who can share this cost. It makes it much harder to have comprehensive teaching books or libraries of training videos.

That is why I'm so happy with the massive level of investment the Ember community pours into shared solutions. Shared solutions are harder to create than one-offs, but they're incredibly valuable.

]]>
<![CDATA[Frameworks are where we iterate on tomorrow's universal abstractions]]>

The amount of complexity a single human programmer can handle is fundamentally limited. Until our brave cyborg future comes, even the best programmers can only hold so much in their minds at once. And the number of programmers that can productively work on a single project is limited by the

]]>
https://eaf4.com/frameworks-are-where-we-iterate-on-tomorrows-universal-abstractions/5a846a12f911ac0018cfb96aFri, 09 Sep 2016 20:11:08 GMT

The amount of complexity a single human programmer can handle is fundamentally limited. Until our brave cyborg future comes, even the best programmers can only hold so much in their minds at once. And the number of programmers that can productively work on a single project is limited by the N^2 communication cost of adding more people.

Therefore, the only way we can make programming go faster is to give programmers more powerful abstractions that encapsulate complexity. And this is in fact how the whole history of programming has evolved.

This is a slow and messy process, because an abstraction isn't great until it has both broad social consensus and mature implementations. The best abstractions are utterly taken for granted and are universally understood by large communities of people.

There is a bootstrapping problem here: abstractions can't be great until a lot of people share them, but you can't get a lot of people to share them until they're great. Frameworks exist to solve this problem.

A framework arises when a community of people agree to share some new abstractions. The abstractions are too new to be universal across all of programming, but they're universal within the community. This allows an ecosystem to grow as if the abstractions were universal, which is the best way to test their power.

In a sense, a framework allows a group of people to collectively share a simulation of one possible future of programming. And by tweaking the simulation, they can iterate toward abstractions that are good enough to truly become the future of programming. The community needs to maintain enough stability to let a rich ecosystem grow, balanced with enough innovation to jettison abstractions that don't work.

In the Ember community, our mantra is "stability without stagnation", and this balance has allowed us to ship ambitious capabilities like the Glimmer rendering library, Ember Fastboot, and EmberCLI while still maintaining a deep addon ecosystem and allowing even many of the biggest, oldest Ember apps to upgrade on a predictable schedule instead of being left behind.

]]>