与成千上的facebook工程师在这纷繁复杂的产品线中相互协作,
代码质量通常面临独特的挑战。

Facebook里,上千名工程师工作在不同的产品线上,为全世界的用户提供可靠优质的服务,而我们在代码质量管理方面也面临着独一无二的挑战。不仅仅是因为我们面对的是一个庞大的代码基库,还有日渐增加的各种各样的特性,有时候如果你想去重构提高某一个模块,往往会影响到其他很多模块。具体在CSS而言,我们需要处理上千份不停变化的CSS文件。之前我们着力于通过Code
Review、代码样式规范以及重构等手段协同工作,而保障代码质量,但是还是会有很多的错误悄悄从眼皮底下溜走,被提交进入到代码库里。我们一直用自建的CSS
Linter来检测基本的代码错误与保证一致的编码风格,尽管它基本上已经满足了我们的目标,但还是存在很多的问题,因此我也想在这篇文章里对如何保障CSS的代码质量进行一些讨论。

使用 stylelint检查CSS_StyleLint,stylelint

当你书写大量的CSS代码时,可能会出现不止一个的错误。可能需要某个工具来阻止你CSS书写的错误。

可能,有的时候你的错误真的是一个bug。也有可能仅仅因为草率造成的不一致或者不明确的代码风格。可能它们当中的许多看起来微不足道(取决于你的性子),但是随着代码库的增多以及时间累积,许多人使用时就会做出有丑陋的东西。事情的后果不是你可以想象的。

你尝试去控制自己。你的同事也帮助你,当你游离及时纠正你的错误。但是,你和你的同事都是错误的制造者,所以最后至少在一定程度上都不可避免的失败了。后来,你或者其他人就要解决你页面CSS错误造成的问题。

无论是你或者你同事都不喜欢讨论你犯下的错误,因为这是令人尴尬的事情。甚至有时会令人沮丧或者产生情感破裂。一定的规范有时候对于代码库的维护是有帮助的,如一致的书写风格,可能当手动执行时,看起来有点迂腐乏味。不然它们就会将你平时喜欢的爱出风头,固执的元素展现出来。

另外你可能更喜欢可以及时修正错误,而不是等待代码审查后由别人指出错误后,自己进行修改并声明自己不会再出现此类错误。当你的CSS出现错误时,一个及时的反馈会帮助你节省很多时间。

你所需要的是一个防止错误产生的机器

你需要一个防止错误产生的机器,可以理解CSS并且理解你:你的意图、喜好、主意以及弱点。

这种机器将具有局限性。所有的事物都不是完美的。但是这种局限和你以及你的同事又有所不同。只要是它可以阻止的错误它都会持续阻止,孜孜不倦。同时,你和你的同事可以一直改善机器,扩展它的功能并且削弱其局限性。它是开源的,全世界的人都可以加入其中尽自己的一份力量–其他想要阻止自己出现CSS书写错误的作者。

和其他一样,CSS 作者也需要 linters

我们将这些防止错误出现的程序称为”linters”。Javascript中有几个比较好的linter。尤其是ESLint,它起到的作用如奇迹般,向我们展示了一个好的linter是如此的有用。但是在CSS中,我们就没有这么幸运了,我们的选择十分有限:
基于Ruby的,具有特殊预处理程序的scss-lint和较早的CSS Lint。

但是这都是在PostCSS出现之前。除此之外,PostCSS提供了一些方法,建立更具有良好交互性的CSS工具。它可以将任何类CSS语法解析为抽象语法树(AST)的插件,从而进行分析以及操作。并且利用自定义解析器,PostCSS甚至可以处理不规范的无效模式(如//注释)

成熟的条件已经可以产生一个具有更强大功能的linter —
基于PostCSS的强大功能以及在scss-lint和ESLint最佳功能的启发之下。

我和几个小伙伴一起致力于这个项目,现在我就要开始介绍一下我们开发的工具:
stylelint.

使用stylelint你可以做的事情

以下是尝试于stylelint的功能总结,其中规则多达百余条,并且具有可扩展性。

在这一点,如果你发现自己已经变得有点不耐烦(“Ok,Ok:我相信stylelint将具有奇迹般的工作效果。不需要过多的总结。”)。仅仅跳到下一部分,在这里我仅仅说明一些问题并提供一些提示。

错误捕获

有些stylelint规则旨在找出明显的错误,如拼写错误或者由于你的心烦意乱或者睡眼惺忪时制造的疏漏。例如,你可以禁止空白块,无效的十六进制值,重复的选择器,未命名的动画名称和错误的线性渐变的语法。

其它的规则都是尽自己最大的努力捕捉更细微的错误。这里有一条规则:
当你使用可以覆盖其属性同行(如 margin-top)的速记属性时(如
margin),就会发出警告,因为这可能是由于你的疏忽造成的。另外,还有一种规则会警告你:当出现混乱局面时,如规则A出现在规则B之前,但是实际上覆盖了规则B,因为规则A的的选择器具有更高的优先级(如,规则A为
.foo.bar{···},规则B为 .foo{···})。这是一种十分棘手的情况。

还有一种规则使用了PostCSS的doiuse插件,用于检查你的浏览器是否支持此样式。另外一种则使用了css-colorguard插件用于提示颜色的相似性,以免造成你的混乱使用。(请注意:
这是基于PostCSS之上的stylelint的主要优势之一:相比于其它PostCSS
插件,用很少的努力,stylelint就可以进行提示。)

强制执行最佳实践

如果你在样式表中使用了系统方法,或者对你的代码设置了一个样式指南,你应该取缔这些模式了。stylelint已经提供了这些功能。

首先,你需要狠狠地控制你的选择器。使用stylelint,你可以禁止超过一定特异性的选择器或者在嵌套深度上设置限制。你可以禁止类别选择器(例如没有
id的选择器),并对其余的选择器使用正则表达式进行命名约定。

你可以禁止!important的使用,或者你的浏览器并不支持的brower
hacks。如果你使用Autoprefixer(或者说你应该使用),你可以禁止在源样式表中使用供应商前缀。

如果你想要更加严谨 — 你可以花费一些时间在配置上,以保证绝对的一致性 —
你可以强制执行样式表属性的顺序,并为黑名单,白名单提供属性,值,函数还有单位。

执行代码样式的约定

stylelint具有自动执行代码样式的约定,所以你和你的队友无需主动设置。我们致力于使这些规则更加全面灵活。

这些规则主要针对于空格,但是同样针对于其它的细节,如;引号,大小写字母,在小数前写零,使用关键字以及拼读出值等等。

梦想你和你的队友可以建立一个格式约定(例如我们始终在声明冒号之后留有一个空格),并在你的stylelint配置中进行修改,之后你们就不会为此再次讨论。让其执行于机器王国。

制定以及扩展一切

Nicholas Zakas,ESLint(以及 CSS
Lint)的创作者,写到ESLint的成功在于它的扩展性。stylelint试图遵循ESLint的领先优势,并且提供给CSS
作者一个linter,同样具有扩展性。

你可以书写并且发布自己的规则插件。现在已经具有了一大堆可以使用的;并且我们渴望看到别人的优秀插件。

配置是可扩展的,因此可以共享。至于插件,我们从ESLint了解了这一功能的价值性。检查其中包括WordPress和SUITCSS配置的,并且已经公布的。

如果你不喜欢 stylelint
的内置提示,你可以手工创建属于你自己的风格,甚至可以为你的组织进行创建。你还可以自定义用于提供警告信息的规则。

使用stylelint的API,你可以创建文本编译器的插件,并进行测试使stylelint
融入到你的工作流的每个方面。

如果你有关于stylelint扩展的想法,请让我们知道!

预期问题的答案

在你的心中可能存在几个疑问。这里有几个最为常见问题的解释:

是否可以在SCSS或者Less中使用stylelint?

答案是肯定的,你可以在SCSS中使用stylelint,并且在Less中也得到了支持!自从PostCSS允许自定义解析器,stylelint可以很轻松的支持各种各样的非标准语法

  • 你可以自定义一个PostCSS解析器进行解析。

正因为PostCSS解析器 —
因此stylelint支持SCSS,Less以及新SugarSS。如果你想要实现另外一个自定义语法的支持,你可以通过PostLess得以实现!

当然,还有一定的规则在你的非标准语法面前得到羁绊(如迷惑于Sass id选择器的
#{$interpolation})。因为stylelint试图掩盖我们样式表的样式 –

我们不仅需要处理庞大的数据库, 还需要跟上时代发展的节奏 ——
新增功能的上线、 已有功能的改善, 甚至是产品重组的需要。

Regex is not Enough:之前用的是正则匹配,不咋的啊

一些人使用标准CSS,一些人使用扩展语言如SCSS,一些人使用一些怪异的自定义属性等等

这些难免都会产生一些漏洞需要去填补。但是,我们一直在处理我们找到的这些错误;在此期间的任何规则可以完全被关闭或者逐次样式表或者逐次行的进行禁用。

stylelint是否可以使用未来CSS语法?

是的!类似于上面所述的答案:
stylelint可以理解PostCSS所理解的任何东西,包括启用未来任何的CSS语法(可能通过PostCSS插件)。事实上,一些stylelint规则专门处理未来CSS语法和一些自定义属性。

stylelint配置是巨大的,我应该从哪儿开始呢?

我们建议三种配置方式:

扩展一个发布的配置。我们维持stylelint-配置标准,以便于为用户提供一个固有的基准。并且许多的配置也已经公布。从头开始,一次添加一条规则。默认情况下,没有一条规则被开启,所以通过手动添加规则你就会知道哪一个会被强制执行,并且可以理解你添加的每条规则。启动复制粘贴配置,决定使用哪些选项并选择性进行删除。

值得庆幸的是,你不需要一遍又一遍的书写巨大的stylelint配置。你可以选择一个你喜欢的风格并且可以在任何地方使用它。

运行stylelint最简单的方式?

对于大多数人而言,最简单的方式就是通过它的命令行。

如果你更偏爱gulp插件,你可以使用gulp-stylelint。对于webpack,这里有很多选择的可能性。我们希望这些插件可以激励你们创建其它的stylelint插件,例如,适用于Grunt的插件。(你可以在开源项目中去寻找!)

你也可以使用PostCSS
插件运行stylelint,包括插件中所包含的任何东西。这就意味着你可以在任何可以使用PostCSS(几乎涵盖于每一个编译工具)的地方使用stylelint!

此外,这里也存在一个适用于Atom,Sublime Text,VS
Code的stylelint文本编译插件,以提供最快的反馈。关于更多信息,请查阅
stylelint 网站上的互补工具列表。

如下所示,在命令行中,你所期待看到的结果:

澳门新葡萄京官网注册 1

在Atom中显示如下;

澳门新葡萄京官网注册 2

stylelint是否可以修补我的错误?

不,但是另外一个叫做stylefmt旨在做到这一点。它需要一个stylelint配置 –
十分类似于你在linting使用的 –
并且可以修复任何错误。我们希望随着社区人员的贡献,stylelint可以发展到自动修补违反stylelint规则的错误。请帮他们实现这个目标!

你也可以使用其它的工具,例如CSScomb或者与stylelint联合使用的perfectionlist,自动修复并自动强制休息。

使用linting进行约束补充

在良好的CSS中有巨大数额的约束。这就是为什么我们花费大量的时间讨论
SMACSS, ACSS, BEM, SUITCSS,
ITCSS等等的方法。我们都知道书写糟糕的CSS是十分容易的,所以,如果让我们不再畏惧于CSS样式的书写,我们需要在工作中建立一个智能化的战略并勇敢的坚守下去。

stylelint的目标是自动执行 —— 提供一套核心规则和一个可插拔的框架以便于CSS
作者可以使用来执行自己的战略。

试一试,让我们知道如何为你提供服务。如果你有相关更好的改进想法,如贡献规则、
增强功能、 测试、 修复bug、 文件、
新想法或只是反馈,请给我们提出!这样我们所有级别的开发人员就有工作做了。

stylelint检查CSS_StyleLint,stylelint
当你书写大量的CSS代码时,可能会出现不止一个的错误。可能需要某个工具来阻止你CSS书写的错误。…

对于CSS来说,这意味着成千上万的文件在一个不断变化的状态中。

老的Linter主要是基于很多个正则表达式对CSS中的语法进行提取,大概是这个样子的:

即使我们已经尽力通过代码回顾、风格指导,甚至是重构等方式去确保css代码的质量,然而无意的小错误依然可以让我们措手不及。

preg_match_all(

// This pattern matches [attr] selectors with no preceding selector.

//*.*?*/|{[^}]*}|s([[^]]+])/s,

$data,

$matches,

PREG_SET_ORDER | PREG_OFFSET_CAPTURE);

foreach ($matches as $match) {

if (isset($match[1])) {

raiseError();

}

直到最近,我们使用的还是一款我们自主开发的CSS检测器(CSS
linter)去获取一些基本的错误以保障代码风格的一致。尽管它已经达到了我们的目的,但我们需要的是更强大的解决方案。

基本上一个检测规则就需要添加一个专门的匹配规则,非常不好维护,在性能上也有很大的问题。对于每个规则俺们都需要遍历整个文件,性能差得很。

正则是远远不够的

旧的css检测器基本上是一些正则表达式搜索和替换规则。正确解析CSS不是一个简单的问题,和供应商扩展和定制规格的变化,会计是更具挑战性。

下面是一个旧代码的例子:

preg_match_all(
    // 这条规则匹配的是没有前置选择器的属性选择器
    '//*.*?*/|{[^}]*}|s([[^]]+])/s',
    $data,
    $matches,,k
    PREG_SET_ORDER | PREG_OFFSET_CAPTURE);
    foreach  ($matches as $match)  {
        if (isset($match[1])) {
            raiseError(...);
        }

维护这种神秘的匹配规则维护是极其无趣的。且不说它很难改变和理解,性能也是一个问题。对于每个规则来说,我们不得不一遍又一遍的去遍历整个输入,匹配不同的正则表达式。

##抽象语法树

我们决定新的解决方案是基于一个真实的,遵循规范的CSS解析器。
由于代码必须对这种类型的语法有效解析器工作,我们不能再把我们整个CSS代码库作为一个巨大的文本。
这种新方法将显著改善CSS代码质量打字错误,否则他们就会被忽视,导致默默地进行不正确的视图呈现或行为。

什么样的行为呢?

一些潜伏在我们巨大代码库阴影中的代码可能向下面一样:

{
    display: none;
    background-color: #8B1D3;
    padding: 10px,10px,0,0;
    opacity: 1.0f;}

你能发现细微的差别吗?
属性名输入错误,不正确的十六进制,错误的分隔符——浏览器会忽略这些,这是与我们开发人员的意图相去甚远。

它意识到没多久 PostCSS 将是一个伟大的工作,一个合适的工具,基于标准的CSS解析器与一个优秀的模块化的体系结构。
然后,我们选择 Stylelint 作为我们的CSS检测器。
它基于PostCSS,非常灵活,并且有很好的支持。
类似于基于javascript解析器/过滤器的Esprima 和 ESLint,PostCSS
Stylelint给你访问整个AST(抽象语法树)的权限。
AST节点可以方便地使用任何条件去访问任何节点:我们使用正确的类名了吗?
我们包括正确的抽象了吗? 有弃用或不受支持的扩展吗? 有本地化问题吗?

在下面这个例子中,我们将遍历所有的声明,并找到所有的首字母大写(”text-transform:
uppercase”)的片段。

root.walkDecls(node =>  {
    if (node.prop === 'text-transform' && node.value === 'uppercase') {
        report({  ...  });
    }});

我们甚至可以解析和解构低级的函数例如linear-gradient
这里有一个更复杂的例子:找到linear-gradient函数,并检查它的第一个参数。

// disallow things like linear-gradient(top, blue, green) w. incorrect first valueroot.walkDecls(node => {
    const parsedValue = styleParser(node.value);
    parsedValue.walk(valueNode => {
        if (valueNode.type === 'function' && valueNode.value === 'linear-gradient') {
            const firstValueInGradient = styleParser.stringify(valueNode.nodes[0]);  
            if (disallowedFirstValuesInGradient.indexOf(firstValueInGradient) > -1) {
                report({  ...  });
            }
        }
    });});

所有无论这些CSS代码如何格式化,如何规则、声明和如何绑定,都非常容易理解和更新。

我们可以非常欣喜的看到,通过使用PostCSS and
Stylelint,当我们处理起例如纠正错别字、关键帧数、!important、复杂选择器、非标准属性等任何常见及潜在问题时都变得得心应手起来~

##自定义规则

我们可以通过一个非常便利的内部插件机制(built-in plugin
mechanism)来自定义我们的规则。
我们现有的插件如下:

  • slow-css-properties to warn against performance-sensitive properties
    like opacity or box-shadow (just to be mindful of them)

  • filters-with-svg-files to warn against filters not being supported
    in Edge when referencing SVG files

  • use-variables to warn against values that could be replaced with
    existing constants (we use customvar(...) abstraction)

  • common-properties-whitelist to catch potentially nonexistent
    properties (i.e., typos)

  • mobile-flexbox to catch multi-value flex that’s not supported in
    older mobile browsers

  • text-transform-uppercase to warn against “text-transform: uppercase”
    (which is not internationalization-friendly)

我们为这些Stylelint插件制定了很多和 规则 和 附属条例,试图将它们释放给某个独立的仓库或者是某个线程的Stylelint团队。

##自动替换

我们的静态分析过程的一个非常重要方面是自动格式化。
linter支持修补,如果不一致时,它会询问你是否需要根据规则进行替换。
这可能是一个强大和节省时间的概念。
提交时你想要做的最后一件事是看到检测错误,然后回去修复它们,尤其当此项工作是非常平常的工作,比如字母重新排序规则。通常更好的做法是让一个自动格式化的工具去完成其工作,以节省开发时间。

不幸的是,Stylelint没有内置的自动格式化(可以说,linter不应该关心这样一个任务),所以我们必须重新添加及实现的一些现有Stylelint规则去提升我们对基础设施的支持。同时,我们正在开发一种潜在的,在未来可以为所有用户服务的通用Stylelint。

澳门新葡萄京官网注册 3

Abstract Syntax Tree

测试所有的事情

我们的老版的CSS检测器问题之一是,我们没有单元测试。
这是一个至关重要的事情,因为我们需要对任意的文本进行解析。
在使用Stylelint重写我们的检测器,我们添加了测试每一个规则,确保规则可以获取正确的东西,忽略了无关紧要的东西,并建议合适的替代品。

我们使用一个可信的且易于理解的Jest框架 ,测试起来是这样的:

test.ok('div { background-image: linear-gradient( 0deg, blue, green 40%, red ); }',
    'linear gradient with valid syntax');test.notOk('a { background: linear-gradient(top, blue, green); }',
    message,
    'linear-gradient with invalid syntax');

受够了正则表达式,我们想搞一个更好用的也是更细致的CSS解释器。CSS本身也是一门语言,老把它当做纯文本文件处理也不好,因此我们打算用AST,即抽象语法树的方式构建一个解析器。这种新的处理方式在性能上面有个很不错的提升,譬如我们的代码库中有这么一段CSS代码:

接下来是什么

这个高质量的CSS重写只是第一步。
有很多有用的规则我们计划添加(内置和自定义的)——所有这些旨在抓住常见错误,执行最佳实践和控制代码风格惯例。
我们已经使用JavaScript(通过ESLint)所以我们没有理由不应该用它去处理CSS。

目前检测器已经与Phabricator集成,成为了我们的代码协作工具,所有的的警告/建议都在修订当中。

这使得CSS检测器在工作流中迈进了非常大的一步。

有一个合适的CSS解析器的另一大好处是,它可以收集有关代码库的准确的统计数据。
最近使用的属性/值是什么?
也许他们应该删除或替换(通过电线发送更少的字节)。 最流行的颜色/字体大小/
层级是什么? 也许他们应该被抽象成可重用的组件或变量。 “最重”选择器是什么?
这里也许有一个性能问题等。

所有的这些可以帮助它提高性能和维护。

{

display: none:

background-color: #8B1D3;

padding: 10px,10px,0,0;

opacity: 1.0f;

}

关于react和内联样式?

值得一提的是CSS-in-JS概念 在react社区逐渐流行起来。
现在的情况是,一个CSS检测器的情况在很大程度上还是作为一个无关紧要的css文件检测器。它目前旨在解析传统CSS文件,以后可能渐渐的去适应解析在JS中定义为CSS代码段。不过,通过PostCSS的JS
API,处理起这些来应该是相当简单的。

虽然我们正在Facebook中积极探索CSS-in-JS的方法,它还处在早期的实验阶段,我们仍然有很大的CSS代码库维护。

眼神好的估计才能看出这个代码片段中存在的问题,譬如某个属性名错了、十六进制的颜色代码写错的,分隔符写错了等等。浏览器才不会主动给你报错呢,这样开发者自己也就很难找到错误了。在具体实现上,我们发现PostCSS
是个不错的工具,因此我们选择了Stylelint作为我们新的Linter工具,它是基于PostCSS构建的,非常的灵活,社区也不错。

共同合作

我们很高兴使用开源工具,并尽可能多得到的反馈。衷心希望它可以提供一个坚实的现代规则和准则,让每个人去使用和相互合作。

谢谢JS infra和webspeed团队,和任何一个帮助重写的朋友,Stylelint 的 David
Clark和Richard Hallows。以及使postcss这种神奇的工具成为可能的整个社区。

这是一个真诚合作、共同努力的结果

稿源:众成翻译 译者:merzoo

英文原文:

就像JavaScript中的Esprima以及ESLint一样,Stylelint提供了对于完整的AST的访问方式,能够让你根据不同的情况更快速简单的访问具体的代码节点,譬如现在我们的检测规则写成了这个样子:

root.walkDecls(node = {

if (node.prop === text-transform node.value === uppercase) {

report({

});

}

});

我们也可以传入一些基本的函数,譬如linear-gradient,就像这个样子:

// disallow things like linear-gradient(top, blue, green) w. incorrect
first valueroot.walkDecls(node = {

const parsedValue = styleParser(node.value);

parsedValue.walk(valueNode = {

if (valueNode.type === function valueNode.value === linear-gradient) {

const firstValueInGradient =
styleParser.stringify(valueNode.nodes[0]);

if (disallowedFirstValuesInGradient.indexOf(firstValueInGradient) 1) {

report({

});

}

}

});

});

这样子写出来的检测规则可读性更好,也更好去理解与维护,并且这种方式无论是在怎样的CSS格式化的情况下,以及不管规则和声明放在哪边,都能正常地工作。

Custom rules:自定义规则

我们默认使用了一些Stylelint内置的规则,譬如declaration-no-important,selector-no-universal,
以及 selector-class-pattern。如何添加自定义规则的方法可以参考built-in
plugin mechanism,而我们使用的譬如:

slow-css-properties
来告警一些性能较差的属性,譬如opacity或者box-shadowfilters-with-svg-files
来告警Edge中不支持SVG的过滤use-variables来告警那些可以被内置的常量替换的一些变量common-properties-whitelist
来检测是否有些误写的其实不存在的属性mobile-flexbox
来检测一些不被老版本手机浏览器支持的属性text-transform-uppercase 来告警
“text-transform: uppercase”,这个在某些语言表现的不友好

我们也贡献了部分规则 以及 ad插件itions 给Stylelint。

Automatic replacement:自动替换

我们检测过程中有一个重要的工作就是自动格式化,Linter会在发现某些问题的时候问你是否需要根据规则进行替换,这个功能会节约你大量的手动修改校正的时间。一般来说,我们提交代码之前都会审视下Linter报出的错误,然后去修复这些错误。可惜的是Stylelint并没有内嵌的自动格式化与修复机制,因此我们重写了部分的Stylelint的规则来增加一个自动替换与修复的功能。

Test all the things

我们老的Linter还有个问题就是没有单元测试,这点就好像代码上线前不进行单元测试一样不靠谱。我们面对的可能是任意格式的处理文本,因此我们也要保证我们的检测规则能够适用于真实有效的环境,这里我们是选择了Jest这个测试框架,Stylelint对它的支持挺好的,然后大概一个单元测试是这个样子:

test.ok(div { background-image: linear-gradient( 0deg, blue, green
40%, red ); },

linear gradient with valid syntax);

test.notOk(a { background: linear-gradient(top, blue, green); },

message,

linear-gradient with invalid syntax);

What‘s next

换一个靠谱的CSS
Linter工具只是保证高质量的CSS的代码的第一步,我们还打算添加很多自定义的检测规则来捕获一些常见的错误,保证使用规定的最佳实践以及统一代码约定规范。我们已经在JavaScript的校验中进行了这一工作。

另外对于React社区中存在的CSS-in-JS这种写法,对于CSS
Linter也是个不小的挑战,现在的大部分的Linter都是着眼于处理传统的CSS文件,以后会添加对于JSX的处理规范吧。

作者:王下邀月熊

链接:-frontend-practice-handbook/blob/master/css/advanced/bestpractices/codestyle/improving-css-quality-at-facebook-and-beyond.md