Are you measuring your social media buttons?

I’d like you to go to a Stack Overflow (or any Stack Exchange) question, and see if you spot a nominally major change.  Such as this one, with a nice answer from Raymond Chen (I think it’s been more than month since I recommended his blog, seriously go read it).

Spot it?

Of course not, what we did is remove our social media sharing buttons.

Left is old, right is new.  A number of other instances were also removed.

The Process

I’m a big advocate of arguing from data and while I freely admit there are many things that are hard to quantify, your gains from social media buttons aren’t one of them.

When discussing sharing buttons, be careful about establishing scope.  Keeping or removing social media buttons isn’t about privacy, personal feelings on Twitter/Facebook/G+, or “what every other site does” (an argument I am particularly annoyed by); it’s about referred traffic and people.  There’s a strong temptation to wander into debate, feeling, and anecdotal territory; stick to the data.

You also need to gather lots of data, at Stack Exchange we prefer to slap a pointless query parameter onto urls we’re tracking.  It’s low tech, transparent, and doesn’t have any of the user facing penalties redirects have.  In particular we slapped ?sfb=1, ?stw=1, and ?sgp=1 onto links shared via the appropriate sharing buttons.  Determining click-throughs is just a matter of querying traffic logs with this approach.  We’ve been gathering data on social media buttons in particular for about four months.

Note that we’re implicitly saying that links that are shared that nobody clicks don’t count.  I don’t think this is contentious, spamming friends and followers (and circlers(?), whatever the G+ equivalent is) is a bit on the evil side. Somebody has to vindicate a share by clicking on it, otherwise we call it a waste.

This sort of nonsense isn’t worth it.

With all this preparation we can actually ask some interesting questions; for Stack Exchange we’re interested in how much traffic is coming from social media (this is an easy one), and how many sharers do so through a button.

Traffic gave us no surprises, we get almost nothing from social media.  Part of this is probably domain specific, my friends really don’t care about my knowledge of the Windows Registry or the Diablo III Auction House.  The sheer quantity of traffic coming from Google also drowns out any other source, it’s hard to get excited about tweaking social media buttons to bring in a few thousand extra views when tiny SEO changes can sling around hundreds of thousands.  To put the difference in scale in perspective, Google Search sends 3 orders of magnitude more traffic our way then the next highest referrer which itself sends more than twice what Twitter does (and Twitter is the highest “social” referrer).

Now that I’ve established a (rather underwhelming) upper-bound for how much our share buttons were getting us, I need to look at what portion can be ascribed to the share buttons versus people just copy/pasting.  This is where the slugs come in, presumably no-one is going to add a random query parameter to urls they’re copy/pasting after all.

You basically want to run a query like this:

SELECT *
FROM TrafficLogs
WHERE
-- everybody tries to set Referer so you know you got some juice from them
(RefererHost = 't.co' OR RefererHost = 'plus.url.google.com' OR RefererHost = 'www.facebook.com')
AND
-- Everybody scrapes links that are shared, don't count those they aren't people
UserAgent NOT LIKE '%bot%'
AND
-- Just the share buttons, ma'am
(Query LIKE '%stw=1%' OR Query LIKE '%sfb=1%' OR Query LIKE '%sgp=1%')

Adapt to your own data store, constrain by appropriate dates, etc. etc.  But you get the idea.  Exclude the final OR clause and the same query gives you all social media referred traffic to compare against.

What we found is that about 90% of everyone who does share, does so by copy/pasting.  Less than 10% of users make use of the share buttons even if they’ve already set out to share.  Such a low percentage of an already pretty inconsequential number doesn’t bode well for these buttons.

One final bit of investigation that’s a bit particular to Stack Exchange is figuring out the odds of a user sharing their newly created post.  We did this because while we always show the question sharing links to everyone, the answer sharing links are only shown to their owner and only for a short time after creation.  Same data, a little more complicated queries, and the answer comes out to ~0.4%.  Four out of every thousand new posts* on Stack Overflow get shared via a social media button (the ratio seems to hold constant for the rest of Stack Exchange, but there’s a lot less data to work with so my confidence is lower on them).

Data Shows They’re No Good, Now What?

Change for change’s sake is bad, right?  Benign things shouldn’t be moved around just because, but for social media buttons…

We know our users don’t like them, they’re sleezy (though we were very careful to avoid any of the tracking gotchas of +1 or Likes), and they’re cluttering some of our most important controls (six controls in a column on every question is a bit much, but to be effective [in theory] these buttons have to be prominent).

These buttons start with a lot of downsides, and the data shows that for us they don’t have much in the way of upsides.  So we we could either trudge along telling ourselves “viral marketing” and “new media” (plus we’ve already got them right, why throw them away?), or admit that these buttons aren’t cutting it and remove them.

So, are you measuring the impact of your site’s social media buttons?

You’re probably not in the same space as Stack Exchange, your users may behave differently, but you might be surprised at what you find out when you crunch the numbers.

*By coincidence this number seems to be about the same for questions and answers, it looks like more people seeing question share buttons balances out people being less enthusiastic about questions.


Extending Type Inferencing in C#

A while ago I used the first Roslyn CTP to hack truthiness into C#.  With the second Roslyn CTP dropping on June 5th, now pretty close to feature complete,  it’s time think up some fresh language hacks.

Roslyn What Now?

For those unfamiliar, Roslyn is Microsoft’s “Compiler as a Service” for the VB.NET and C# languages.  What makes Roslyn so interesting is that it exposes considerably more than “Parse” and “Compile” methods; you also have full access to powerful code transformations and extensive type information.

Roslyn is also very robust in the face of errors, making the most sense it can of malformed code and providing as complete a model as possible.  This is very handy for my evil purposes.

Picking A Defect

Now I happen to really like C#, it’s a nice, fast, fairly rapidly evolving, statically typed language.  You get first class functions, a garbage collector, a good type system, and all that jazz.  Of course, no language is perfect so I’m going to pick one of my nits with C# and hack up a dialect that addresses it using Roslyn.

The particular defect is that you must specify return types in a method declaration, this is often pointless repetition in my opinion.

Consider this method:

static MvcHtmlString ToDateOnlySpanPretty(DateTime dt, string cssClass)
{
  return MvcHtmlString.Create(String.Format(@"<span title=""{0:u}"" class=""{1}"">{2}</span>", dt, cssClass, ToDateOnlyStringPretty(dt, DateTime.UtcNow)));
}

Is that first MvcHtmlString really necessarily?  Things get worst when you start returning generic types, oftentimes I find myself writing code like:

Dictionary<string, int> CalcStatistics()
{
  var result = new Dictionary<string, int>();
  // ...
  return result;
}

Again, the leading Dictionary<string, int> is really not needed the type returned is quite apparent so we’re really just wasting key strokes there.

This pointless repetition was addressed for local variables in C# 3.0 with the var keyword.  With Roslyn, we can hack up a pre-processor that allows var as a method’s return type.  The above would become the following:

var CalcStatistics()
{
  var results = new Dictionary<string, int>();
  // ...
  return results;
}

The Rules

Wanton type inferencing can be a bit dangerous, it makes breaking contracts pretty easy to do for one.  So I’m imposing a few constraints on where “var as a return” can be used, for the most part these are arbitrary and easily changed.

  • var can only be used on non-public and non-protected methods
  • if a method returns multiple types, a type will be chosen for which all returned types have an implicit conversion with the following preferences
    • classes before interfaces
    • derived types before base types
    • generic types before non-generic types
    • in alphabetical order
    • with the exceptions that Object is always considered last and IEnumerable (and IEnumerable<T>) are considered before other interfaces
  • a method with empty returns will become void

The last rule may be a little odd as C# doesn’t have a notion of a “void type” really, which is something of a defect itself in my opinion.  Which type is chosen for a return is well-defined so it’s predictable (always a must in a language feature) and attempts to match “what you meant”.

I made one more handy extension, which is allowing var returning methods to return anonymous types.  You can sort of do this now either passing around Object or using horrible grotty hacks (note, don’t actually do that); but since there’s no name you can’t do this cleanly.  Since var returning methods don’t need names, I figured I might as well address that too.

Let’s See Some Code

Actually loading a solution and parsing/compiling is simple (and boring), check out the source for how to do it.

The first interesting bit is finding all methods that are using “var” as a return type.

// eligibleMethods is a List<MethodDeclarationSyntax>
var needsInferencing =
 eligibleMethods
 .Where(
  w => (w.ReturnType is IdentifierNameSyntax) &&
  ((IdentifierNameSyntax)w.ReturnType).IsVar
 ).ToList();

This literally says “find the methods that have return type tokens which are ‘var’”.  Needless to say, this would be pretty miserable to do from scratch.

Next interesting bit, we grab all the return statements in a method and get the types they return.

var types = new List<TypeInfo>();
// returns is a List<ReturnStatementSyntax>
foreach (var ret in returns)
{
  var exp = ret.Expression;

  if (exp == null)
  {
    types.Add(TypeInfo.None);
    continue;
  }

  var type = model.GetTypeInfo(exp);
  types.Add(type);
}

Note the “model.GetTypeInfo()” call there, that’s Roslyn providing us with detailed type information (which we’ll be consuming in a second) despite the fact that the code doesn’t actually compile successfully at the moment.

We move onto the magic of actually choosing a named return type.  Roslyn continues to give us a wealth of type information, so all possible types are pretty easy to get (and ordering them is likewise simple).

var allPossibilities = info.SelectMany(i => i.Type.AllInterfaces).OfType<NamedTypeSymbol>().ToList();
allPossibilities.AddRange(info.Select(s => s.Type).OfType<NamedTypeSymbol>());
// info is an IEnumerable<TypeInfo>
foreach (var i in info)
{
  var @base = i.Type.BaseType;
  while (@base != null)
  {
    allPossibilities.Add(@base);
    @base = @base.BaseType;
  }
}

Rewriting the method with a new return type is a single method call, and replacing the “from source” method with our modified one is just as simple.

And that’s basically it for the non-anonymous type case.

Anonymous Types

For returning anonymous types, everything up to the “rewrite the method” step is basically the same.  The trouble is, even though we know the “name” of the anonymous type we can’t use it.  What we need to do instead is “hoist” the anonymous type into an actual named type and return that instead.

This is kind of complicated, but you can decompose it into the following steps:

  1. Make a note of returned anonymous types
  2. Rewrite methods to return a new type (that doesn’t exist yet)
  3. Rewrite anonymous type initializers to use the new type
  4. Create the new type declaration

Steps #1 and #2 are easy, Roslyn doesn’t care that your transformation doesn’t make sense yet.

Step #3 requires a SyntaxRewriter and a way to compare anonymous types for equality.  The rewriter is fairly simple, the syntax for an anonymous type initializer and a named one vary by very little.

Comparing anonymous types is a little more complicated.  By the C# spec, anonymous are considered equivalent if they have the same properties (by name and type) declared in the same order.

Roslyn gives us the information we need just fine, so I threw a helper together for it:

internal static bool AreEquivalent(TypeSymbol a, TypeSymbol b, Compilation comp)
{
  var aMembers = a.GetMembers().OfType<PropertySymbol>().ToList();
  var bMembers = b.GetMembers().OfType<PropertySymbol>().ToList();
  if (aMembers.Count != bMembers.Count) return false;
  for (var i = 0; i < aMembers.Count; i++)
  {
    var aMember = aMembers[i];
    var bMember = bMembers[i];
    if (aMember.Name != bMember.Name) return false;
    if (aMember.DeclaredAccessibility != bMember.DeclaredAccessibility) return false;
    var aType = aMember.Type;
    var bType = bMember.Type;
    var aName = aType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
    var bName = bType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
    if (aName == bName) continue;
    var conv = comp.ClassifyConversion(aType, bType);
    if (!conv.IsIdentity) return false;
  }
  return true;
}

Notice that Roslyn also gives us details about what conversions exist between two types, another thing that would be absolutely hellish to implement yourself.

The final step, adding the new type, is the biggest in terms of code although it’s not really hard to understand (I also cheat a little).  Our hoisted type needs to quack enough like an anonymous to not break any code, which means it needs all the expected properties and to override Equals, GetHashCode, and ToString.

This is all contained in one large method.  Rather than reproduce it here, I’ll show what it does.

Take the anonymous type:

new
{
A = "Hello",
B = 123,
C = new Dictionary<string, string>()
}

This will get a class declaration similar to

internal class __FTI88a733fde28546b8ae4f36786d8446ec {
  public string A { get; set; }
  public int B { get; set; }
  public global::System.Collections.Generic.Dictionary<string, string> C { get; set; }
  public override string ToString()
  {
    return new{A,B,C}.ToString();
  }
  public override int GetHashCode()
  {
    return new{A,B,C}.GetHashCode();
  }
  public override bool Equals(object o)
  {
    __FTI88a733fde28546b8ae4f36786d8446ec other = o as __FTI88a733fde28546b8ae4f36786d8446ec;
    if(other == null) return new{A,B,C}.Equals(o);
    return
      (A != null ? A.Equals(other.A) :
      (other.A != null ? other.A.Equals(A) : true )) &&
      B == other.B &&
      (C != null ? C.Equals(other.C) : (other.C != null ? other.C.Equals(C) : true ));
  }
}

The two big cheats here are the lack of a constructor (so it’s technically possible to modify the anonymous type, this is fixable but I don’t think it’s needed for a proof-of-concept) and using the equivalent anonymous type itself to implement the required methods.

Conclusion

I’m pretty pleased with Roslyn thus far, it’s plenty powerful.  There are still a bunch of limitations and unimplemented features in the current CTP though, so be aware of them before you embark on some recreational hacking.

In terms of complaints, some bits are a little verbose (though that’s gotten better since the first CTP), documentation is still lacking, and there are some fun threading assumptions that make debugging a bit painful.  I’m sure I’m Doing It Wrong in some places, so some of my complaints may be hogwash; I expect improvements in each subsequent release as well.

If it wasn’t apparent, the code here is all proof-of-concept stuff.  Don’t use in production, don’t expect it to be bug free, etc. etc.

You can check out the whole project on Github.


More, a CSS compiler

It’s hard to represent CSS in pictures, so I’m breaking this post up with puppies.

CSS is an… interesting technology.  As Wil Shipley put it, “CSS: If a horse were designed by a committee of camels.”  There’s just enough rough edges, weird decisions, and has-not-had-to-use-it-everyday blindness to make you want something better to work in.  While it’s still a draft (and I hope has no chance of becoming part of a standard), take a look at CSS Variables and despair that that was proposed.

I’m hardly the first (second, or probably even millionth) person to think this, so there are some alternatives to writing CSS out there.  At Stack Exchange we use LESS (specifically the dotLESS variant), Sass is also pretty popular just from straw polling developers though I’m not really familiar with it.

For kicks I started throwing my own CSS compiler together a few months ago that’s a little more radical than LESS (from which I took a great deal of inspiration).  I’m calling it More for now because: naming is hard, and I like the homage to LESS.  Although I am drawing explicit inspiration from LESS, More is not a LESS super-set.

The radical changes are

  • Order doesn’t matter
  • “Resets” are first class
  • Sprites are first class
  • Explicit copy selector syntax, in addition to mixins
More details below, as you might guess familiarity with LESS will make lots of this sound really familiar.  If you’d rather not read a 3,000+ word post, hop over to Github where the README takes a whack at summarizing the whole language.

Declaration Order Doesn’t Matter

In CSS subsequent declarations override preceding ones, if you declare .my-class twice the latter one’s rules win; likewise for rules within the same block.  This is pretty wrong-headed in my opinion, you should be stating your intentions explicitly not just tacking on new rules.  Combine this with how negative an impact bloated CSS can have on page load speeds (CSS and HTML are the bare minimum necessary for display, everything else can be deferred), I can’t help but classify this as a misfeature.

In More, the order of selector blocks in the output is explicitly not guaranteed to match that of the input.  Resets, detailed below, allow you to specify that certain blocks preceed all others and @media blocks necessarily follow all others but any other reliance on ordering is considered a bad practice.

Likewise, overriding rules in blocks is an explicit operation rather than a function of declaration order, the specifics are detailed below.

He wants your CSS structure to be explicit.

Resets

More natively supports the concept of a “reset”.  Blocks contained between @reset{…} will always be placed at the top of generated CSS file, and become available for inclusion via reset includes.

@reset{
  h1 { margin: 0; }
}
.class h1 { color: blue; }

becomes

h1 { margin: 0; }
.class h1 { color: blue; }

Blocks within a @reset block cannot use @reset includes or selector includes, but can use mixins.  Reset blocks are also not referenced by selector includes.

@reset includes let you say “reset this block to the default stylings”, practically this means including any properties defined within a @reset{…} in the block with the @reset include.

@reset {
  h1 { margin: 0; }
}
.class h1 { color: blue; @reset(h1); }

becomes

h1 { margin: 0; }

.class h1 { color:blue; margin: 0; }

It is not necessary to specify the selector to reset to, an empty @reset() will reset to the selector of the block that contains it.  Note that for nested blocks this will be the inner most blocks selector.

@reset {
  :hover { color: red; }
  h1 { margin: 0; }
}
h1 {
  @reset(); 
  line-height: 15px; 
}
a {
  color: blue;
  &:hover {
    font-weight: bold;
    @reset();
  }
}

becomes

:hover { color: red; }
h1 { margin: 0; }
h1 { margin: 0; line-height: 15px; }
a { color: blue; }
a:hover { font-weight: bold; color: red; }

Properties included by a @reset() (with or without optional selector) will never override those defined otherwise in a block.

@reset { a { color: red; height: 10px; } }
a { @reset(); color: blue; } }

becomes

a { color: red; height: 10px; }
a { color: blue; height: 10px; }

The intent behind resets is to make it easier to define a style’s “default element stylings” and subsequent reset to them easily.

You know what’s cool? Not generating your sprite files independent of the CSS that uses them.

Sprites

If you include a non-trivial number of images on  a site, you really ought to be collapsing them into sprites.  There are plenty of tools for doing this out there, but tight integration with CSS generation (if you’ve already decided to compile your CSS) is an obvious win.

To that end, More lets you generate sprites with the following syntax:

@sprite('/img/sprite.png'){
  @up-mod = '/img/up.png';
  @down-mod = '/img/down.png';
}

This declaration creates the sprite.png from up.png and down.png, and adds the @up-mod and @down-mod mixins to the file.  Mixins created from sprites are no different than any other mixins.

For example:

.up-vote {
  @up-mod();
}

could compile to

.up-vote {
  background-image: url(img/sprite.png);
  background-position: 0px 0px;
  background-repeat: no-repeat;
  width: 20px;
  height: 20px;
}

For cases there are other properties that you want to include semantically “within” a sprite, these generated mixins take one mixin as an optional parameter.  The following would be valid, for example.

.down-vote {
  @down-mod(@myMixin);
}

Finally, there are command line options for running executable on generated sprite files.  It’s absolutely worthwhile to squeeze the last few bytes out of a PNG file you’ll serve a million times, but actually implementing such a compression algorithm is outside the scope of More.  While this can be fairly easily hacked together, as a built-in I hope it serves as a “you should be doing this” signal to developers.  I personally recommend PNGOUT for this purpose.

Include Selectors

Sometimes, you just want to copy CSS around.  This can be for brevity’s sake, or perhaps because you want to define a set of selectors in terms of some other selector.   To do that with More, just include the selector in @() like so:

div.foo { color: #abc; }
div.bar {
  background-color: #def;
  @(div.foo);
}

This syntax works with more complicated selectors as well, including comma delimited ones, making it possible to include many distinct blocks in a single statement.  It is not an error if a selector does not match any block in the document.

Selector includes are one of the last things to resolve in a More document, so you are copying the final CSS around.  @(.my-class) will copy the final CSS in the .my-class block, but not any of the mixin invokations, nested blocks, or variable declarations that went into generating it.

For example:

.bar {
  foo: @c ?? #eee;
  &:hover {
    buzz: 123;
  }
}
.fizz {
  @c = #aaa;
  @(.bar);
}

Will generate:

.bar { foo: #eee; }
.bar:hover { buzz: 123; }
.fizz { foo: #eee; }

As @(.bar) copies only the properties of .bar in the final document.

Not stolen, inspired. *cough*

LESS Inspired Bits

That’s it for the really radical new stuff, most of the rest of More is heavily inspired by (though rarely syntactically identical to) LESS.  Where the syntax has been changed, it has been changed for reasons of clarity (at least to me, clarity is inherently subjective).

More takes pains to “stand out” from normal CSS, as it is far more common to be reading CSS than writing it (as with all code).  As such, I believe it is of paramount importance that as little of a file need be read to understand what is happening in a single line of More.

This is the reason for heavy use of the @ character, as it distinguishes between “standard CSS” with its very rare at-rules and More extensions when scanning a document.  Likewise, the = character is used for assignment instead of :.

Variables

More allows you to define constants that can then be reused by name throughout a document, they can be declared globally as well as in blocks like mixins (which are detailed below).

@a = 1;
@b = #fff;

These are handy for defining or overriding common values.

Another example:

@x = 10px;
img {
  @y = @x*2;
  width: @y;
  height: @y;
}

It is an error for one variable to refer to another in the same scope before it is declared, but variables in inner scopes can referrer to those in containing scopes irrespective of declaration order.  Variables cannot be modified once declared, but inner variables can shadow outer ones.

Puppies, (debatably) not annoying.

Nested Blocks

One of the annoying parts of CSS is the repetition when declaring a heirarchy of selectors.  Selectors for #foo, #foo.bar, #foo.bar:hover, and so on must appear in full over and over again.

More lets you nest CSS blocks, again much like LESS.

#id {
  color: green;
  .class {
    background-color: red;
  }
  &:hover {
    border: 5px solid red;
  }
}

would compile to

#id {
  color: green;
}
#id .class {
  background-color: red;
}
#id:hover {
  border: 5px solid red;
}

Note the presense of LESS’s & operator, which means “concat with parent”.  Use it for when you don’t want a space (descendent selector) between nested selectors; this is often the case with pseudo-class selectors.

Mixins

Mixins effectively lets you define functions which you can then include in CSS blocks.

@alert(){
  background-color: red;
  font-weight: bold;
}
.logout-alert{
  font-size: 14px;
  @alert();
}

Mixins can also take parameters.

@alert(@c) { color: @c; }

Parameters can have default values, or be optional altogether.   Default values are specified with an =<value>, optional parameters have a trailing ?.

@alert(@c=red, @size?) { color: @c; font-size: @size; }

Mixin’s can be passed as parameters to mixins, as can selector inclusions.

@outer(@a, @b) { @a(); @b(green); }
@inner(@c) { color: @c; }
h1 { font-size: 12px; }
img { @outer(@(h1), @inner); }

Will compile to

h1  { font-size: 12px; }
img { font-size: 12px; color: green; }

As in LESS, the special variable @arguments is bound to all the parameters passed into a mixin.

@mx(@a, @b, @c) { font-family: @arguments; }
p { @mx("Times New Roman", Times, serif); }

Compiles to

p { font-family: "Times New Roman", Times, serif; }

Parameter-less mixins do not define @arguments, and it is an error to name any parameter or variable @arguments.

Puppy function: distract reader from how much text is in this post.

Functions

More implements the following functions:

  • blue(color)
  • darken(color, percentage)
  • desaturate(color, percentage)
  • fade(color, percentage)
  • fadein(color, percentage)
  • fadeout(color, percentage)
  • gray(color)
  • green(color)
  • hue(color)
  • lighten(color, percentage)
  • lightness(color)
  • mix(color, color, percentage/decimal)
  • nounit(any)
  • red(color)
  • round(number, digits?)
  • saturate(color, percentage)
  • saturation(color)
  • spin(color, number)

Most of these functions are held in common with LESS, to ease transition.  All functions must be preceded by @, for example:

@x = #123456;
p {
   color: rgb(@red(@x), 0, 0);
}

Note rgb(…), hsl(..), and other CSS constructs are not considered functions, but they will convert to typed values if used in variable declarations, mixin calls, or similar constructs.

In String Replacements

More strives to accept all existing valid CSS, but also attempts to parse the right hand side of properties for things like math and type information.  These two goals compete with each other, as there’s a lot of rather odd CSS out there.

img {
  filter: progid:DXImageTransform.Microsoft.MotionBlur(strength=9, direction=90);
  font-size: 10px !ie7;
}

It’s not reasonable, in my opinion, to expect style sheets with these hacks to be re-written just to take advantage of More, but it can’t parse them either.

As a compromise, More will accept any of these rules but warn against their use.  Since a fancier parsing scheme isn’t possible, a simple string replacement is done instead.

img {
  filter: progid:DXImageTransform.Microsoft.MotionBlur(strength=@strength, direction=90);
  font-size: @(@size * 2px) !ie7;
}

The above demonstrates the syntax.  Any variables placed in the value will be simply replaced, more complicated expressions need to be wrapped in @().

Although this feature is intended mostly as a compatibility hack, it is also available in quoted strings.

Other, Less Radical, Things

There are a couple of nice More features that aren’t really LESS inspired, but would probably fit right in a future LESS version.  They’re not nearly as radical additions to the idea of a CSS compiler.

Dressing up your puppies, optional.

Optional Values

Mixin invocations, parameters, variables, and effectively whole properties can be marked “optional”.

.example {
  color: @c ?? #ccc;
  @foo()?;
}

This block would have a color property of #ccc if @c were not defined, making overrides quite simple (you either define @c or you don’t).  Likewise, the foo mixin would only be included if it were defined (without the trailing ? it would be an error).

A trailing ? on a selector include will cause a warning, but not an error.  It doesn’t make sense, as selector includes are always optional.

Entire properties can be optional as follows:

.another-example {
  height: (@a + @b * @c)?;
}

If any of a, b, or c are not defined then the entire height property would not be evaluated.  Without the grouping parenthesis and trailing ? this would result in an error.

Overrides

When using mixins, it is not uncommon for multiple copies of the same property to be defined.  This is especially likely if you’re using mixins to override default values.  To alleviate this ambiguity, it is possible to specify that a mixin inclusion overrides rules in the containing block.

@foo(@c) { color: @c; }
.bar {
  color: blue;
  @foo(red)!;
}

This would create a .bar block with a single color property with the value of “red”.  More will warn, but not error, when the same property is defined more than once in a single block.

You can also force a property to always override (regardless of trailing ! when included) by using !important.  More will not warn in these cases, as it’s expressed intent.

You can also use a trailing ! on selector includes, to the same effect.

Puppies With Hats

Values With Units

Values can be typed, and will be correctly coersed when possible and result in an error otherwise.  This makes it easier to catch non-nonsensical statements.

@mx(@a, @b) { height: @a + @b; }
.foo{
  @mx(12px, 14); // Will evaluate to height: 26px;
  @mx(1in, 4cm); // Will evaluate to height: 6.54cm; (or equivalent)
  @mx(15, rgb(50,20,10)); // Will error
}

Similarly, any function which expects multiple colors can take colors of any form (hsl, rgb, rgba, hex triples, or hex sextuples).

When needed, units can be explicitly stripped from a value using the built in @nounit function.

Includes

You can include other More or CSS files using the @using directive.  Any included CSS files must be parse-able as More (which is a CSS super-set, although a strict one).  Any variables, mixins, or blocks defined in any includes can be referred to at any point in a More file, include order is unimportant.

@using can have a media query, this results in the copied blocks being placed inside of a @media block with the appropriate query specified.  More will error if the media query is unparseable.

At-rules, which are usually illegal within @media blocks, will be copied into the global context; this applies to mixins as well as @font-face, @keyframes, and so on.  This makes such at-rules available in the including file, allowing for flexibility in project structure.

More does allow normal CSS @imports.  They aren’t really advisable for speed reasons.  @imports must appear at the start of a file, and More will warn about @import directives that had to be re-ordered as that is not strictly compliant CSS in the first place.

CSS3 Animations

More is aware of the CSS3 @keyframes directive, and gives you all the same mixin and variable tools to build them.

@mx(@offset) { top: @offset * 2px; left: @offset + 4px; }
@keyframes my-anim {
  from { @mx(5); }
  to { @mx(15); }
}

Compiles to

@keyframes my-anim {
  from { top: 10px; left: 9px;}
  to { top: 30px; left: 19px; }
}

It is an error to include, either via a mixin or directly, a nested block in an animation block.  More will also accept (and emit unchanged) the Mozilla and Webkit prefixed versions of @keyframes.

Variables can be declared within both @keyframes declarations and the inner animation blocks, the following would be legal More for example.

@keyframes with-vars {
  @a = 10;
  from { 
    @b = @a * 2;
    top: @b + 5px;
    left: @b + 3px;
  }
  to { top: @a; left: @a; }
}

It is an error to place @keyframes within @media blocks or mixins.  As with @media, @keyframes included via a @using directive will be pulled into the global scope.

More Is A CSS Superset

More strives to accept all existing CSS as valid More.  In-string replacements are a concession to this, as is @import support; both of which were discussed above.  @charset, @media, and @font-face are also supported in pursuit of this goal.

@charset may be declared in any file, even those referenced via @using.  It is an error for more than one character set to be referred to in a final file.  More will warn if an unrecognized character set is used in any @charset declaration.

@media declarations are validated, with More warning on unrecognized media types and erroring if no recognized media types are found in a declaration.  Blocks in @media statements can use mixins, but cannot declare them.  Selector includes first search within the @media block, and then search in the outer scope.  It is not possible for a selector include outside of a @media block to refer to a CSS block inside of one.

By way of example:

img { width: 10px; @(.avatar); }
p { font-color: grey; }
@media tv {
  p { font-color: black; }
  .avatar { border-width: 1px; @(p); }
  .wrapper { height:10px; @(img); }
}

Will compile to:

img { width: 10px; }
p { font-color: grey; }
@media tv {
  p { font-color: black; }
  .avatar { border-width: 1px; font-color: black; }
  .wrapper { height:10px; width: 10px; }
}

@font-face can be used as expected, however it is an error to include any nested blocks in a @font-face declaration.  More will warn if a @font-face is declared but not referred to elsewhere in the final CSS output; this is generally a sign of an error, but the possibility of another CSS file (or inline style) referring to the declared font remains.  More will error if no font-family or src rule is found in a @font-face block.

The pun is the highest form of humor.

Minification

More minifies it’s output (based on a command line switch), so you don’t need a separate CSS minifier.  It’s not the best minifier out there, but it does an OK job.

In particular it removes unnecessary white space, quotation marks, and reduces color and size declarations; “green” becomes #080 and “140mm” become “14cm” and so on.

A contrived example:

img
{
  a: #aabbcc;
  b: rgb(100, 50, 30);
  c: #008000;
  d: 10mm;
  e: 1.00;
  f: 2.54cm;
}

Would become (leaving in white space for reading clarity):

img 
{
  a: #abc;
  b: #64321e;
  c: green;
  d: 1cm;
  e: 1;
  f: 1in;
}

This minification is controlled by a command line switch.

Cross Platform

For web development platform choice is really irrelevant and acknowledging that More runs just fine under Mono, and as such works on the Linux and Mac OS X command lines.

More Isn’t As Polished As LESS

I always feel a need to call this out when I push a code artifact, but this isn’t a Stack Exchange thing.  This is a “Kevin Montrose screwing around with code to relax” thing.  I’ve made a pretty solid effort to make it “production grade”, lots of tests, converted some real CSS (some of which is public, some of which is unfortunately not available), but this is still a hobby project.  Nothing is going to make it as solid as years of real world usage, which LESS and Sass have and More doesn’t.

Basically, if you’re looking for something to simplify CSS right now and it cannot fail go with one of them.  But, if you like some of what you read and are looking to play around well…

Give it a Spin

You can also check out the code on Github.


Committing A Horrible Atrocity Against C#

Title from the other side of the tracks: Bringing Sanity To C# Conditions

Have you played with Roslyn?  It’s Microsoft’s “compiler as a service” framework coming to a future .NET release, with a CTP having been available for awhile now.

The focus seems to be on making it really easy to do the sorts of source transformations Visual Studio (and heavy hitting plugins like ReSharper) specialize in.  Better code generation, static analysis, and what-have-you tooling will be probably be nice result as well.

But you can do some really evil/awesome things as well thanks to Roslyn failing very gracefully in the presence of malformed code, by design.

For example, have you ever wanted C# to be truthy?

I’m told my sense of fun is a little warped.

Truthiness is coalescing certain values to true/false that are not strictly boolean; making null a stand in for false, for example.  This is a common feature in dynamic languages, Python, Ruby, and of course Javascript all have some notion of truthiness.

I personally kind of hate truthiness (I’d appreciate it as an explicit unary operator though), but I realize this is a religious position.  I also hate mayonnaise, but don’t begrudge it’s existence.

But if you do like truthiness, or like writing Fun code like I do, Roslyn makes it possible to cram it into C#.

I’m choosing to define Truthiness In C# as:

  • null, the empty string, and the defaults of ValueTypes are false
  • everything else is true
  • only available in control statements, not as a type coercion

This definition is really easy to change, for reasons that will become apparent later, and is basically Python’s take (minus collections and dictionaries) so it’s not too crazy.

I want this to be a build step, so the flow for compiling with truthiness is:

  1. Take a (possibly invalid due to truthy statements) C# program
  2. Find all the bits that assume truthiness
  3. Replace those bits with equivalent and valid vanilla C#
  4. Hand the results off to the actual C# compiler

Roslyn actually can emit assemblies (in theory, I haven’t tried in the CTP), but for the sake of brevity I’m choosing to stop the process early and write new .cs files to disk.

Finding bits that assume truthiness is quite simple, because Roslyn doesn’t just blow up on an invalid program; it does it’s best to give you everything that can be gleaned from malformed code, just like Visual Studio.

This let’s us use a SyntaxWalker to visit each node of the AST for our dodgy program and still mostly make sense of it.  If we encounter anything where we expect a conditional (inside an if statement, for example) that isn’t provably a boolean, then we’ve probably found a truthy usage.  We don’t do anything with that knowledge yet, we just stash it away for later.

The SyntaxWalker that does this is surprisingly simple.  Once you’ve got a SemanticModel, figuring out the type of an expression is trivial.

private bool IsBool(ExpressionSyntax exp)
{
  var info = Model.GetSemanticInfo(exp);
  var knownType = info.Type;

  return
    knownType != null &&
    knownType.SpecialType == Roslyn.Compilers.SpecialType.System_Boolean;
}

Once we’ve walked all source, finding a list of all truthy things, we can do the actual truthy implementation.  The easiest way to do this is to wrap all truthy values in a call to method that implements the above rules.  A more “correct” way would be to transform the truthy expressions into proper boolean ones, saving a method call and giving the compiler more information to work with.

I naturally went with the easy way, adding this method to every class that uses truthy expressions.  This also makes it very easy to change the truthiness rules, as I alluded to earlier.

private static bool __Truthy(object o)
{
  if (o == null || (o is string && (string)o == string.Empty)) return false;
  var type = o.GetType();
  if (type.IsValueType)
  {
    return !o.Equals(Activator.CreateInstance(type));
  }
  return true;
}

There’s a bit of finese in the actual wrapping of expression in calls to __Truthy.  If an expression contains a sub-expression that is itself truthy we want to replace the sub-expression first and re-walk the SyntaxTree.  This is because whether or not an expression is truthy is dependent on it’s sub-expressions: (true || “”) is truthy, but (true || __Truthy(“”)) is not essentially.  There’s also a little bit of work spent detecting if something is already being passed to __Truthy, so we don’t end up with __Truthy(__Truthy(“”)) or similar; this is mostly caused by ternary conditionals just being weird relatively speaking.

The full project on Github is an executable that transforms a whole Visual Studio .csproj, in a rather haphazard fashion.  You need the referenced assemblies to get a SemanticModel, which I’m extracting from .csprojs for convenience.

To illustrate the transformation, here’s some truthy C#.

static void Main(string[] args)
{
  UserDefined ud = new UserDefined { ABoolProp = false, AStringProp = "" };
  bool a = true, b = false;

  if ("hello world") Console.WriteLine("String literals are truthy");
  if (1) Console.WriteLine("int literals are truthy");
  if (!null) Console.WriteLine("null is falsy");
  if (!ud.AStringProp) Console.WriteLine("Member access works");
  if (ud.AStringProp.Length || !ud.ABoolProp) Console.WriteLine("Chained member access works");

  if (a || b || 0) Console.WriteLine("Normal bools aren't mangled");
  if (true) Console.WriteLine("Normal literals aren't mangled");

  string str = a ? "hello" : "world";

  if (str == "hello") Console.WriteLine("Normal ternary conditionals aren't mangled");

  for (int i = 0; i < 3 && (ud.AnIntMethod() ? "hello" : "world"); i++)
  {
    Console.WriteLine(i + " complicated condition");
  }
}

This gets transformed into

static void Main(string[] args)
{
  UserDefined ud = new UserDefined { ABoolProp = false, AStringProp = "" };
  bool a = true, b = false;

  if (__Truthy("hello world")) Console.WriteLine("String literals are truthy");
  if (__Truthy(1)) Console.WriteLine("int literals are truthy");
  if (!__Truthy(null)) Console.WriteLine("null is falsy");
  if (!__Truthy(ud.AStringProp)) Console.WriteLine("Member access works");
  if (__Truthy(ud.AStringProp.Length) || !ud.ABoolProp) Console.WriteLine("Chained member access works");

  if (a || b || __Truthy(0)) Console.WriteLine("Normal bools aren't mangled");
  if (true) Console.WriteLine("Normal literals aren't mangled");

  string str = a ? "hello" : "world";

  if (str == "hello") Console.WriteLine("Normal ternary conditionals aren't mangled");

  for (int i = 0; i < 3 && (__Truthy(__Truthy(ud.AnIntMethod()) ? "hello" : "world")); i++)
  {
    Console.WriteLine(i + " complicated condition");
  }
}

Notice that spacing is maintained, Roslyn round trips that sort of thing which is very nice.

And of course, the code runs and outputs the expected text:

String literals are truthy
int literals are truthy
null is falsy
Member access works
Chained member access works
Normal bools aren't mangled
Normal literals aren't mangled
Normal ternary conditionals aren't mangled
0 complicated condition
1 complicated condition
2 complicated condition

There are, as always, caveats.  Lots in this case, as Roslyn is still a CTP and there are a number of C# features it doesn’t handle yet.  Using var or lambdas will mess things right up, and I’m sure there are some out right bugs in both Roslyn and my dinky code.

But isn’t it cool that it’s possible to abuse C# even this much already?


Stack Exchange API V2.0: Throttling

Here’s a post I lost while writing the rest of the Stack Exchange API V2.0 series, it’s been restored (ie. I hit the publish button) in glorious TechniColor.

Every API has some limit in the number of requests it can serve.  Practically, your servers will eventually fall down under load.  Pragmatically, you’ll want to limit access to keep your servers from falling down under load.  Rationing access to an API is a tricky business; you have to balance technical, business, and philosophical concerns.

History

In early versions of the Stack Exchange API we had two distinct throttles, a “simultaneous requests” throttle that would temporarily ban any IP making more than 30 requests per second and a “daily quota” throttle that would drop any requests over a certain threshold (typically 10,000) per day.

In V2.0, the simultaneous request throttle remains and unauthenticated applications (those making requests without access tokens) are subject to the same IP base daily quota that was in V1.x.  We’ve also added two new classes of throttles for authenticated applications, a per-app-user-pair quota and a per-user quota.

Function

When an authenticated app makes a request it is counted against a per-user-app-pair daily quota (notably not dependent upon IP address) and a per-user daily quota.  The user-app pair numbers are returned on each request (in fields on the common wrapper object), but the per-user daily value is hidden.

The authenticated application cases are a bit complicated, so here’s a concrete example:

A user is actively using 6 applications, all of which are making authenticated requests.  The user’s daily quota is 50,000 and each application has a daily quota of 10,000 (these numbers are typical).  Every time an application makes a request it receives its remaining quota with the response, and this number should be decremented by one on each request; if application #1 makes its 600th request it will receive “9,400” back, if application #2 simultaneously made its 9,000th request it will get “1,000” back.  If an application exceeds its personal quota (with its 10,001st request) its request will begin being denied, but every other application will continue to make requests as normal. However, if the sum of the requests made by each application exceeds the user’s quota of 50,000 then all requests will be denied.

There are good reasons to throttle.

Rationale

The simultaneous request throttle is purely technically driven, it helps protect the network from DOS attacks.  This throttle is enforced much higher in the stack than the others (affected requests never even hit our web tier), and is also much “ruder”.  In a perfect world we’d return well formed errors (as we do elsewhere in the API) when this is encountered, but practically speaking it’s much simpler to just let HAProxy respond however it pleases.

Our daily quotas are more tinted philosophically.  We still need to limit access to a scarce resource (our servers’ CPU time) and we don’t want any one application to monopolize the API to the detriment of others (or direct users of the Stack Exchange sites).  A simple quota system addresses these concerns sufficiently in my opinion.  It’s also “fair” since all you have to do is register on Stack Apps, after which you have access to all of our CC-wiki’d data.  The quota of 10,000 was chosen because it allows an app to request (100 at a time) one million questions a day via the API; it’s a nice round number, and is also fairly “absurd” giving us some confidence that most applications can function within the quota.

The per-app-user-pair quota starts pulling some business sense into the discussion.  In essence, IP based throttles fall apart in the face of computing as a service (Google App Engine, Azure, Amazon Web Services, and others).  While we were aware of this flaw in v1.0, we had no recourse at the time (lacking the resources to add authentication to the API and still ship in a timely manner).  By excluding services like App Engine from our API consumers we were hamstringing them, which made improving our throttling story a high ticket item for API v2.0.  As a rule of thumb, if a feature makes life easier on the consumers of your API you should seriously investigate it; after-all an API is not in and of itself interesting, the applications built on top of it are.

With the per-app-user-pair quota alone we still have to worry about DOS attacks, thus the per-user quota.  One wrinkle is in not reporting the state of the quota to applications.  I considered that, but decided that it was something of a privacy leak and not of interest to most applications; after all, there’s no way for one application to control another.


An Absurd Experiment In String Parsing

I’m going to start this off by saying that this post deals with some code that is a bit… silly.  Definitely veering into “why would you do this?” territory for most, but it’s still an interesting endeavor.

The question is, how quickly can you parse a string?

To give some context, at Stack Exchange we’ve got a process that’s slurping logs from our load balancer into SQL Server for us to query; it’s useful for analytics, debugging, and performance monitoring.  The trick is that at our scale this is quite a lot of data to be dealing with, and traffic spikes at peak time have a habit of knocking the service into a death spiral.

Investigations into one of the latest incidents of service suicide indicated, for a short time, that actually parsing the logs was a bottleneck.  Ultimately this turned out to be a red-herring (the actual killer was, rather predictably, disk IO), but the seed of “how would we do this faster?” was planted.

To be clear, what follows isn’t solving a real problem; this is just me having some fun with code.  Everyone should do it now and again, builds character.

There are, broadly, two approaches to string parsing

You can either go with regular expressions, or you can roll it yourself with IndexOf (or your framework of choice’s equivalent).  All things being equal, I’m inclined to go with regular expressions for their terseness and comparable ease of maintainability.

If you’re going for maximum speed (as I am here), you really do want to do all the string manipulation yourself and maintainability be damned.  However, it’d be nice if we could get the performance of IndexOf’ing and Substring’ing everywhere with a cleaner interface.

Writing IL, approximately as intense as this song.

Enter IL

ILGenerator, my favorite class to go out of my way to avoid, lets you generate new methods at the bytecode (Common Intermediate Language, in the .NET world) level at runtime.  If you want speed, you’ll find it here.

The approach I went with was to create a nice interface (call chaining, opaque state objects, and all that jazz) for describing a parser, and at the last minute dropping into some hideous IL generation to create an actual delegate to do the deed.  This produces very, very fast string parsing code without being unbearable to use; it also enables some dirty tricks we wouldn’t want to use even in hand rolled parsing code, which I’ll get to later.

In terms of capabilities, I decided that a reasonable string parser would be composed of the following components

  • Moving forward or backwards in the string a fixed number of characters
  • Moving forward or backwards in the string until a certain string is encountered
  • Taking a given number of characters from the string as a value
  • Taking characters from the string until a certain string is encountered
  • Taking the remainder of the string as a value
  • Perform an arbitrary action when the input string does not conform to the parser’s expectations

I decomposed these six components further into eighteen different method calls, though there are many convenience overrides (referring to a member via either a string or a MemberInfo being the most common).

It ends up looking like the following (contrived example) in practice:

var parser =
 FSBuilder
  .Take(":=", "VarName")
  .Take("(", "MethodName")
  .Take(")", "Parameters")
  .Until(";")
  .TakeRest("Remained")
  .Else((str, obj) => { throw new ArgumentException(); })
  .Seal();

var myObj = new MyObject();
parser("a:=fx(a,b,c);   ... and then some more ...", myObj);

Do note that this isn’t meant to be a replacement for regular expressions, it’s meant to replace a class of string parsers for which I’d expect most developers to use regular expressions.  I do suspect these operations are enough to build DFAs (though I’m not going to spend time trying to prove it, I don’t really care), but not very cleanly and most regular expression engines aren’t strictly regular anyway.

This code runs like greased lightening

On my machine, parsing “(\d+),(\d+),(\d+),(\d+)” and it’s equivalent hand written and composed versions across one million randomly generated (but compliant) strings yields the following.

  • Regex: 3540ms
  • Hand Written: 1140ms
  • Composed: 690ms

I’m dropping the last digit, as it’s highly variable, and taking the median of five runs; forcing a GC (and pausing for it to complete) between each run to try and minimize GC jitters.

Wait, aren’t the hand written and composed versions equivalent?

They’re definately similar, but there are a lot of dirty tricks you can pull off when you’re emitting IL that you wouldn’t really want to tolerate in hand written code.

A big one, if you’re using IndexOf and Substring you’re still creating lots of strings (creating GC pressure) and implicitly paying for method calls (which are fast but not instant, so they add up over many iterations) to get at actual character data.  The internals of a regular expression are even worse in terms of overhead and object churn.  The IL I’m generating does everything in terms of character arrays (in fact, I can guarantee I create only one to parse a string of integers) and avoids method calls like the plague, effectively inlining everything.

In a related vein, both regular expression and IndexOf/Substring approaches end up giving you strings you need to parse into the appropriate data types.  Naively, you’ll typically use things like Int32.Parse which have the same “strings + method call” overhead as above.  Generating IL lets me inline my own parsing code, which naturally deals in character arrays, avoiding both costs again.

The IndexOf/Substring approach does still eek out better performance when you’re just dealing with string members.  Delving into the .NET classes with Reflector shows that Microsoft has (wisely) invested quite a lot of time in optimizing string manipulation, to match them I’d probably have to start linking C in which is a bigger time investment than I’m willing to make.

There really is no lower limit.

Of course, it could be dirtier

I have left some areas unexplored that would probably squeeze a few more milliseconds out.

Non-integer types still fall back to Parse calls, as do DateTime and TimeSpan and some cases of enumerations.  Alternative implementations are time consuming to produce, but would pay dividends especially in the DateTime and TimeSpan cases.

I suspect allocations can be reduced even further through some dubious stackalloc or ThreadLocal usage, possibly combined with some heap juggling.  Not nearly as certain these would work out for the best, but a lack of expertise and time have kept from digging too deeply.

My string scanning algorithm is basically a for loop, breaking out something like Boyer-Moore would pay noticeable dividends for larger “needles”.  This is a lot of complexity for exactly zero gain in the rather common “single character needle” case, so I haven’t sunk the necessary time in yet and may never.

Not much time has been spent on generating ideal IL either.  There’s certainly a lot of stack shuffling that could be eliminated for truly minuscule, if non-zero, savings.

If you’re interested, play around with the code

I’ve thrown it up on github, and uploaded a package on NuGet as the unexcitingly named FluentStringParser (because naming is hard).  The code’s moderately well documented, but it’s still IL so the learning curve is more of a brick wall really.  Still, learn by doing and all that.

I do stress that this is a personal project, not a Stack Exchange endorsed chunk of code (those are currently documented in this blog post).  Caveat emptor if you pull this code in as it does dirty, dirty things for silly, silly reasons.


Stack Exchange API V2.0: The Stable Future

This is the last of my planned series about v2.0 of the Stack Exchange API (the contest is still going on, and we froze the interface a few weeks ago), and I’d like to talk about some regrets.  Not another mistakes post (API V2.0 is still too young to judge with the benefit of hindsight), but something in a similar vein.

This is thankfully a pretty short post, as I’ve worked pretty hard to reduce the regrettable things in every iteration of our API.

The need for breaking changes

I definitely tried to break as little as possible, compare the question object in V1.1 to that in V2.0 to see how little changed.  However, we ate two big breaks: restructuring the common response wrapper, and moving the API from per-site urls (api.stackoverflow.com, api.serverfault.com, etc.) to a single shared domain (api.stackexchange.com).

These were definitely needed, and I’m certain benefits they give us will quickly pay off the transition pain; but still, breaking changes impose work on consumers.  When building an API it’s important to never make breaking changes wantonly, you’re making non-trivial imposition on other developer’s time.

We couldn’t do everything

I’ve already got a list of things I want in V2.1, unfortunately some of them were things I’d like to have gotten into V2.0.  A truism of development is that there’s always more to be done, real developers ship, and so on.  API development is no different, but I’m hoping to see an increase in new features and a decrease in time between API revisions as Stack Exchange matures.

We should be much stabler now

Naturally, since I’m aware of these problems we’ve taken steps to make them less of an issue.

I’m very satisfied with the current request wrapper, and the filter feature (plus our backwards compatibility tooling around it) makes it much easier to make additions to it without breaking existing consumers.  In general, filters allow for lots of forward compatibility guarantees.

We’ll never be able to do everything, but all sorts of internal gotchas that made V1.1 really rough to roll out have been fixed as part of V2.0.  Subsequent minor revisions to V2.0 shouldn’t be nearly as rough, and with our growing staff developer resources should be comparatively plentiful.


Follow

Get every new post delivered to your Inbox.