Maintaining a legacy Haskell app

as a junior developer

https://slides.com/emhoracek/haskell-24/live

1. A project

2. How we did it

3. Challenges

4. Strategies that helped

What I'm going to talk about

Background

  • When I joined in 2015, Haskell was small % of work
  • When Daniel Patterson left in 2016, he handed the Haskell projects off to me
  • Most were dormant, but Jacobin Magazine had one project coming up

Jacobin "Redesign"

Before

WordPress with Haskell app embedded in iFrames

Haskell app

WordPress

Haskell app

Goals

  • WordPress is just for editors and writers (not readers)
  • Posts/articles are structured, with different types of posts structured in different ways
  • All pages use the same template and style language
  • Mobile-friendly and responsive

Haskell app serves content from WordPress API

Jacobin "Redesign"

After

Haskell app

WordPress

How we did it

https://slides.com/emhoracek/haskell/live

WordPress

  • WP REST API
  • Advanced Custom Fields
"acf": {
  "internal_memo": "",
  "sections": [
    {
       "acf_fc_layout": "standard",
       "title": "(section title)",
       "featured_media": false,
       "section": [
         {
           "content": "(section content)"
         }
       ]
    }
  ],
  "paywall": false,
  "toc": false,
  "subhead": "(subhead)",
  "antescript": "",
  "postscript": "",
  "footnotes": ""
  /* and many more */
}

Offset templates

<wpPostByPermalink>

  <apply template="_post">

    <wpMultisection>
      <wpSections>
        <wpSection>
          <section class="po-cn__section po-wp__section" id="ch-${wpSectionsIndex}">
            <if exists="${wpTitle}">
              <then><h1 class="po-cn__subhead po-wp__subhead"><wpTitle/></h1></then>
              <else><hr class="po-cn__rule po-wp__rule"/></else>
            </if>
            <wpContent/>
          </section>
        </wpSection>
      </wpSections>
    </wpMultisection>

  </apply>

</wpPostByPermalink>

Custom Fields with Offset

multiSectionFields :: Field Ctxt
multiSectionFields = CN "multisection"
                       ["acf"]
                       [ M "sections"
                         [ F "title"
                         , M "section"
                           [ F "content" ] ] ]
data Field s = F Text -- A single flat text or number field
             | P Text (Text -> Fill s) -- A custom-parsed flat field
             | N Text [Field s] -- A nested object field
             | C Text [Text] -- A text field found by following the specified path
             | CN Text [Text] [Field s] -- Combination of `C` and `N`
             | M Text [Field s] -- A list field, where each element is an object

Custom Fields with Offset

data Field s = F Text -- A single flat text or number field
             | N Text [Field s] -- A nested object field
             | C Text [Text] -- A nested text field found by following the specified path
             | CN Text [Text] [Field s] -- Combination of `C` and `N`
             | M Text [Field s] -- A list field, where each element is an object
             | P Text (Text -> Fill s) -- A custom-parsed flat text field
             | B Text -- A single flat boolean field
             | CB Text [Text]  -- Combination of `C` and `B`
             | PV Text (Maybe Value -> Fill s) -- A custom-parsed value field
             | PN Text (Object -> Fill s) -- A custom-parsed nested field
             | PM Text ([Object] -> Fill s) -- A custom-parsed list field
             | Q Text IdToEndpoint -- A field that will make another request
             | QM Text IdsToEndpoint -- A list of fields that will make another request

The end result

Challenges

https://slides.com/emhoracek/haskell-24/live

The Genius-Oh-No Cycle

"I have no idea what I'm doing"

  • Me: Working on a team with front-end developers
  • David: Working with this weird templating language

Me: "How have your teams handled stuff like this?"

David: "The templating languages are more flexible than Larceny, and the frameworks make it easier to expose more data."

Me: "Oh. We can do that."

David: "We can?!"

We write wpCustom, which allows Remeike to query any endpoint he wants and use any JSON from the response!

<wpCustom endpoint="wp/v2/posts/${wpId}">
  <ul>  
    <wpTags>
      <li><a href="/tag/${wpSlug}"><wpName/></a></li>
    </wpTags>        
   </ul>
</wpCustom>       
{ id: 43453,
  /* ...lots of other fields... */
  tags: [
	{ name: "Sports", slug: "sports"
	  name: "Bookmarx", slug: "bookmarx" }
   ]
}

A few weeks later,
a wpCustom template
crashes the entire site.

The Genius-Oh-No Cycle

Chesterton's Fence

https://slides.com/emhoracek/haskell-24/live

According to my research the fence was built as an art project and abandoned in 1975!

THERE WAS A RAMPAGING BULL BEHIND THE FENCE

idk what if it's important?????

who knows????

I'll research the historical owners of the surrounding property.

What is the difference between

"this is bad"

and

"I don't understand this"?

Stereotype threat

https://slides.com/emhoracek/haskell-24/live

Things that helped

https://slides.com/emhoracek/haskell-24/live

Pair programming

Test-driven development

The TDD Cycle

Green

Red

Refactor

Reflect

RFCs

For companies:

Can't find a person with extensive production experience developing FP web applications? Try:

 

a person with knowledge of FP

plus

a person with experience in web applications

 

It works!

Finally

  • I work at Position Dev
    • positiondev.com / @positiondev
  • Twitter: @horrorcheck
  • Slides: slides.com/emhoracek/haskell/live
  • Offset: github.com/positiondev/offset
  • Larceny: github.com/positiondev/larceny
  • THANKS: David Hartunian, Daniel Patterson, Remeike Forbes, Pea Lutz, Bhaskar Sunkara

abstractions

By emhoracek

abstractions

  • 1,225