Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal: Plug-ins v2 and Thin Buffalo Binary #1791

markbates opened this issue Sep 19, 2019 · 3 comments


Copy link

@markbates markbates commented Sep 19, 2019

The current plug-in system for Buffalo is slow, error prone, and confusing for many users. This is a proposal for a alternative approach to plug-ins that, hopefully, addresses these issues.

As well as proposing a major revision of plug-ins, this proposal also introduces the concept of a "thin" Buffalo binary.

In this proposal I will use as a library, package, and tool, that some people may not want to use.

The Fat Binary

Let's start with the buffalo binary. It is a "fat" binary. This means that it contains everything, from the web-server, to the generators, etc... Because of this several problems arise. The first is that this "fat" binary contains a lot of dependencies, regardless of whether the end user will ever use those features.

The current binary has a hard dependency on pop. The buffalo generate resource command, for example, generates pop style resources. So if the app in question uses another ORM, or none at all, or if they only generate JSON, then this dependency shouldn't be present.

Another problem this "fat" binary presents is that it can often be a version mismatch with the app it is being run against. If the app is v0.12.x but the binary is v0.15.0, then the results of running buffalo generate resource, for example, wouldn't be guaranteed to be consistent.

The Plug-in Problem.

Buffalo plugins have a set of rules that must be followed for them to be consider, by Buffalo, as a plugin.

  • Plug-ins must be named in the format of buffalo-<plugin-name>. For example, buffalo-myplugin.
  • Plug-ins must be executable and must be available in one of the following places:
    • if not set, $GOPATH/bin, is tried
    • in the ./plugins folder of your Buffalo application
  • Plug-ins must implement an available command that prints a JSON response listing the available commands.

When a command is run, such as buffalo db migrate, Buffalo will try and find the plug-in (binary) and if found, it will then try and shell out the command of migrate to it.

This constant shelling out and searching for executables is error prone, confusing, slow, and again, has similar versioning concerns to that of the buffalo binary itself.

Thin Binary, Fat Binary

To solve these problems, I suggest the following.

First, we make the buffalo binary a "thin" one. We remove anything needed for running inside of an application. For example, buffalo dev is not required outside of an application, so why is it needed in the binary?

The buffalo binary should only contain what it needs when running its "outside" duties, such as buffalo new.

Instead, I propose using a similar approach to

The "thin" buffalo binary when a command such as buffalo db migrate would [hands wave wildly in the air] compile a "fat" buffalo specifically for the current application, using the versions of Buffalo, and other libraries declared in the go.mod.

The pop plugin would be added to the application via a standard import.

// <app>/plugins/plugins.go
package plugins

import _ ""

The buffalo-pop plugin would register any sub-commands it may have for the new "fat" binary that is built.

Other Changes

With these changes the buffalo-plugins.toml file will no longer be needed, as well as all of the buffalo plugin sub-commands.

Sub-commands would be forced under their plug-in's name. So if the buffalo-pop plugin is registered with the name "pop" then the buffalo db migrate command would become buffalo pop migrate. Gone, also, would be "wrapping" another command.

Interfaces would be used to allow for simplier access points into the plug-in system. For example, we might have interfaces for such things as listing any commands, or implementing a "fix" command for things like buffalo fix to hook into.

// Not final interfaces, just rough ideas.

type Plugin interface {
  Name() string

type Fixable interface {
  // optional interface for hooking into "fix" sub-command
  // buffalo fix (will also call buffalo-pop/plugin.Plugin#Fix - if implemented)
  Fix(context.Context) error

type Commanding interface {
  Commands() (Commands, error)


I would love to hear community feedback on this proposal. Also, if there is anyone out there that would want to take lead on such a proposal, please speak up. :)


This comment has been minimized.

Copy link

@dnnrly dnnrly commented Sep 20, 2019

Nice, that makes a lot of sense! I can see this solving PATH related problems well.


This comment has been minimized.

Copy link

@timuckun timuckun commented Sep 20, 2019

Here is a crazy idea.

Why not have a fat buffalo for development and a thin one for production? Maybe they could be managed with symlinks or a shim shell script.


This comment has been minimized.

Copy link

@hubyhuby hubyhuby commented Sep 24, 2019

Food for thoughts from a humble user of buffalo.
Discussing with friends using Ruby on rail or Laravel, the discussion ends always at one point on how "plugins-gems-modules" makes them sooo productive.
Most importantly the plugins are well supported & updated; therefore previously generated apps can be MAINTAINED easily.

On the long run , the success of buffalo will depend in part on those plugins, their number, quality, and how easy they are to maintain.

I am not expert in the guts of the Buffalo BIN, but the proposal seems to go in the way of an easier maintenance of plugins; which is great.
Things like being able to launch a project in 0.12.x with a buffalo Bin in 0.15.x would be great.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
None yet
4 participants
You can’t perform that action at this time.