McCord Unveils Phoenix LiveView to Challenge JavaScript Single-Page Apps

ElixirConf 2018 - Keynote - Chris McCord — ElixirConf

ElixirConf

Server-side stateful rendering on the BEAM matches client-side application interactivity while eliminating complex JavaScript frameworks by streaming real-time template diffs over WebSockets.

AI-assisted summary of ElixirConf 2018 - Keynote - Chris McCord — original source. Check the original for full context.

Source published:

Key ideas

  • Phoenix 1.4 approaches release with native HTTP/2 support, faster compilation, a route-listing 404 page, and a modular transport layer refactored by José Valim to ease custom WebSocket implementations. (0:00)
  • The new open-source Telemetry library, created in collaboration with Erlang Solutions and Plataformatec, introduces an unopinionated, macro-free core dispatch API designed to unify ecosystem-wide metrics collection. (4:49)
  • Chris McCord unveils Phoenix LiveView, a server-rendered real-time UI framework running over WebSockets that delivers rich interactivity using standard Elixir GenServers without requiring dedicated client-side JavaScript frameworks. (15:18)
  • A stress test porting Marko.js's 60 FPS rainbow animation across 85 dynamic elements demonstrates that BEAM processes and microsecond template rendering can handle extreme server-driven DOM updates at scale. (40:41)

Detailed notes

## Background This keynote address by Chris McCord was published on YouTube by the ElixirConf channel on September 8, 2018. The presentation runs for nearly 58 minutes and features no formal video description or show notes provided by the uploader.

## TL;DR Chris McCord begins with ecosystem milestones, detailing the near-completion of Phoenix 1.4, updates to the official Programming Phoenix book, and the newly collaborative Telemetry project for unified metrics. He then unveils the keynote's centerpiece: Phoenix LiveView, a server-driven UI library designed to provide reactive, real-time user experiences over WebSockets without the operational overhead and toolchain fatigue of modern client-side JavaScript single-page applications. McCord demonstrates LiveView's capabilities through real-time form validation, multi-tab presence tracking, and an absurd 60 FPS animation rendered directly on the BEAM, before outlining its architectural trade-offs, performance advantages, and multi-node clustering strategies.

## Atmosphere & style The presentation is candid, enthusiastic, and laced with self-deprecating humor—ranging from McCord joking about caffeine overdoses to getting playfully reprimanded by José Valim over slide formatting. There is an unmistakable undercurrent of ambition as McCord challenges the status quo of modern front-end complexity, eliciting excitement and amusement from the audience with live demos that push the boundaries of server rendering. It feels like an invigorating, paradigm-shifting community keynote that blends pragmatic engineering advice with an inspiring call to advance the state of the art.

## Bulletin - [00:00](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=0s) Phoenix 1.4 approaches release with native HTTP/2 support, faster compilation, a route-listing 404 page, and a modular transport layer refactored by José Valim to ease custom WebSocket implementations. - [04:49](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=289s) The new open-source Telemetry library, created in collaboration with Erlang Solutions and Plataformatec, introduces an unopinionated, macro-free core dispatch API designed to unify ecosystem-wide metrics collection. - [15:18](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=918s) Chris McCord unveils Phoenix LiveView, a server-rendered real-time UI framework running over WebSockets that delivers rich interactivity using standard Elixir GenServers without requiring dedicated client-side JavaScript frameworks. - [40:41](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=2441s) A stress test porting Marko.js's 60 FPS rainbow animation across 85 dynamic elements demonstrates that BEAM processes and microsecond template rendering can handle extreme server-driven DOM updates at scale.

## Key takeaways - [00:00](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=0s) Phoenix 1.4 features native HTTP/2 support, significantly faster development compilation, and an interactive 404 page that lists available router routes. - [00:00](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=0s) José Valim refactored the Phoenix 1.4 transport layer, eliminating the need to copy-paste core implementations to extend WebSockets and resolving a race condition in the JavaScript Presence client. - [04:49](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=289s) The Telemetry project establishes a minimal, macro-free event dispatch contract across the Elixir ecosystem, executing zero-cost ETS lookups when no handlers are attached. - [09:41](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=581s) Standardized Telemetry enables actionable, automated BEAM diagnostics—such as detecting atom table exhaustion or message queue bottlenecks—directly inside browser dashboards. - [11:09](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=669s) Phoenix LiveView traces its origins to Sync, a 2013 Ruby library that hit insurmountable concurrency roadblocks under MRI and EventMachine, ultimately leading McCord to Elixir. - [18:57](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=1137s) LiveView addresses front-end complexity creep by replacing multi-tiered SPA toolchains, GraphQL schemas, and Node.js SSR layers with server-side rendered stateful components. - [21:16](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=1276s) LiveView ports the React component model to the server, using the ~L sigil and GenServer processes to push minimal template diffs over WebSockets directly to the DOM. - [25:10](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=1510s) Declarative HTML attributes like phx-click and phx-change map browser interactions directly to handle_event/3 server callbacks, enabling instant inline Ecto validations without client JavaScript. - [29:00](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=1740s) The phx-submit binding allows forms to persist directly within the existing LiveView process, keeping localized Gettext error translations entirely on the server. - [35:05](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=2105s) Combining Phoenix PubSub contexts with LiveView enables real-time, cross-tab synchronization and automatic page redirects in fewer lines of code than traditional CRUD controllers. - [38:59](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=2339s) Integrating Phoenix Presence inside server-side LiveViews enables instant multi-tab presence tracking without writing custom client reconciliation code in JavaScript. - [40:41](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=2441s) A 60 FPS server-rendered rainbow animation benchmark proves the BEAM runtime can compute layout transforms and push real-time WebSocket diffs at 16.6 ms intervals. - [45:35](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=2735s) LiveView is not designed for offline-first apps or low-latency optimistic UI, but offers unmatched velocity and built-in SEO for standard forms, dashboards, and CRUD flows. - [48:18](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=2898s) Phoenix measures template rendering in microseconds rather than milliseconds, providing the underlying speed necessary to support streaming DOM diffs across thousands of clients. - [48:59](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=2939s) Framework performance matters beyond marketing benchmarks because sub-millisecond execution unlocks entirely new architectural paradigms like server-driven real-time interfaces. - [54:54](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=3294s) LiveView leverages distributed Erlang and Phoenix PubSub to support multi-node clusters behind sticky WebSocket load balancers using serialized server process tokens.

## Detailed walkthrough ### [00:00](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=0s) Introduction and Phoenix 1.4 Update

Chris McCord opens the keynote by teasing the theme of "going full circle," noting that while the audience does not yet know what that phrase means, it will become clear later in the talk. He plans to unveil exciting new features that the team has been working on, but explains that understanding the motivation requires traveling back in time to what originally brought him to Elixir and Phoenix. Before diving into the history, he gives credit to his employer, DockYard, highlighting that they allow him to dedicate the majority of his time to open-source development while serving clients ranging from early-stage startups to Fortune 100 enterprises.

Turning to Phoenix 1.4, McCord addresses the anticipation built up over his previous conference talks. Phoenix 1.4 is practically finished, with only a single open issue on the milestone tracker—a code formatter issue that would take only ten minutes to complete. McCord jokes that there is already an open pull request for it, but if he merges and closes it, people will demand to know why the final version has not been published yet. The real bottleneck is bandwidth: between preparing a keynote, writing a book, and finishing framework development, the team needs time to ensure documentation is fully prepared so they do not have to immediately rush out a 1.4.1 release. Although he hoped to drop a release candidate on stage "Apple-style," a release candidate is expected within the week, and the master branch is already very stable.

Phoenix 1.4 introduces several major enhancements: HTTP/2 support, faster development compilation times, and quality-of-life additions such as displaying all available router routes directly on the 404 error page during development. A major architectural improvement is the overhaul of the transport layer, executed primarily by José Valim. McCord describes his own development process as top-down: he envisions the high-level API first—such as typing `channel` and expecting bi-directional communication to immediately work—and builds downward into lower-level dependencies like PubSub without focusing on broader design abstractions. Valim subsequently refactored the transport layer, extracting emerging patterns into clean, extensible, modular pieces. As a result, extending WebSocket transports no longer requires copy-pasting the framework's entire transport implementation. McCord also shares a mixed update regarding the Presence API: applications using the existing JavaScript library contain an inherent race condition, but the newly rewritten JavaScript fixes the issue while simplifying the API.

“With Phoenix 1.4, José has pretty much completely rewritten the transport layer so now if you want to extend the WebSocket transport, you used to have to like copy paste our entire WebSocket transport, now there's much less code for you to write.” — Chris McCord

- **DockYard Sponsorship**: Supports McCord's full-time open-source work across startups and Fortune 100 projects. - **Phoenix 1.4 Core Enhancements**: - Native HTTP/2 support. - Significantly faster development compilation cycles. - Interactive 404 debug page listing all configured router routes. - Extensible, modular transport layer refactored by José Valim. - Bug fix resolving a race condition in the JavaScript Presence client library alongside a cleaner API surface.

---

[04:02](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=242s) Programming Phoenix >= 1.4 Book Update

McCord provides an update on the official book, *Programming Phoenix*. The release had been substantially delayed; originally planned as a Phoenix 1.3 edition, the authors decided to skip version 1.3 entirely and focus on *Programming Phoenix >= 1.4* to ensure the content remains future-proof.

Only three chapters remain to be written. The plan is to release new chapters on the Pragmatic Bookshelf platform every two weeks, bringing the beta release to completion within six to eight weeks, followed by a final print publication before the end of the year. McCord shares an ElixirConf attendee discount code (`phoenixworkshop2018`) valid for approximately five days, directing attendees to the shortcut link `phoenixframework.org/book`.

“It was supposed to be Programming Phoenix 1.3, and it is now Programming Phoenix greater than 1.4, so we just skipped the 1.3 and went straight for a book that should be relatively future-proofed.” — Chris McCord

---

[04:49](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=289s) The Telemetry Project: Unified Metrics for the Ecosystem

McCord transitions to a long-standing initiative: establishing a universal system for collecting and reporting metrics within the Elixir ecosystem. While metrics have been discussed at previous conferences, the Phoenix team previously lacked the bandwidth to build an overarching solution. Erlang Solutions stepped up to collaborate with the Phoenix team, with José Valim and Plataformatec providing guidance and mentorship. Over the preceding three months, this initiative culminated in an open-source project named **Telemetry**.

The fundamental problem Telemetry aims to solve is fragmentation. The Elixir community has access to a wealth of operational data and numerous third-party backends—such as StatsD, Prometheus, and New Relic—but libraries have historically instrumented and exposed metrics in inconsistent, ad-hoc ways. Telemetry provides an unopinionated, rock-solid, and minimal core dispatch API. Unlike Phoenix's older macro-heavy instrumentation, Telemetry requires no metaprogramming. Publishers execute events using `telemetry:execute/3`, and consumers simply register standard module-function callbacks via `attach`. If no handlers are attached, dispatch acts as an almost zero-cost no-op lookup in ETS.

Because Telemetry remains completely framework-agnostic, any library across the ecosystem—including Ecto, Plug, Absinthe, and HTTP clients like Hackney—can depend on this tiny core without risk of frequent breaking changes. Higher-level concerns, such as system probing, request-per-second rate calculation, aggregations, and vendor-specific reporting (StatsD, Prometheus), reside in separate companion packages layered on top of the Telemetry foundation. This architecture frees framework maintainers to focus on extracting rich runtime data to deliver actionable application insights.

“Telemetry's goal is to be this initial bare-bones metrics library that everyone can use, that way we have a common interface to start collecting and reporting metrics.” — Chris McCord

```mermaid flowchart TD accTitle: Telemetry Event Dispatch Architecture accDescr: Diagram illustrating how ecosystem libraries emit events through Telemetry's core API to user handlers and external reporting packages.

subgraph Producers ["Ecosystem Event Producers"] P1["Phoenix (e.g., [:phoenix,:request,:stop])"] P2["Ecto (e.g., [:ecto,:query])"] P3["HTTP Clients / Absinthe"] end

subgraph Core ["Telemetry Core API"] T["telemetry:execute(event, measurements, metadata)"] ETS[("Fast ETS Handler Lookup")] end

subgraph Consumers ["Consumers & Reporters"] H["User Defined Callback Functions"] R1["StatsD Reporter"] R2["Prometheus Reporter"] end

P1 --> T P2 --> T P3 --> T T --> ETS ETS -->|"Dispatch (if registered)"| H ETS -->|"Forward"| R1 ETS -->|"Forward"| R2 ```

- **Collaborative Effort**: Joint initiative between Erlang Solutions, Plataformatec (José Valim), and the Phoenix core team. - **Design Philosophy**: Minimalistic, zero-macro, rock-solid core interface avoiding breaking changes for foundational libraries. - **Performance Characteristics**: Emitted events with zero attached listeners perform an ETS table lookup and immediately no-op, minimizing runtime overhead. - **Decoupled Architecture**: Separation between the core dispatch mechanism (`telemetry`) and external aggregators, gauges, or metrics reporters (StatsD, Prometheus).

[09:41](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=581s) Phoenix Telemetry and VM Insights for Non-Experts

At DockYard, Chris McCord and his team frequently work with client organizations that have had Elixir running in production for years without having internal BEAM expertise. Many of these teams do not know what the Erlang runtime atom table is or how it behaves, and they remain unaware when their applications leak memory. Instead, when an application occasionally runs out of memory and crashes, the automated deployment or container orchestrator simply restarts the node; developers shrug off the transient downtime because they lack accessible tooling to diagnose the root cause. McCord argues that this diagnostic information has always been present in the runtime, but developers need high-level tools that automatically surface actionable insights directly in their browser interfaces.

With the unified Telemetry effort underway, McCord envisions a real-time Phoenix telemetry dashboard that connects to runtime metrics and translates raw VM statistics into clear human guidance. Rather than forcing engineers to dig into low-level runtime monitors, the system can use straightforward heuristics to diagnose common failure patterns. If the atom table reaches 90% capacity, the dashboard should explicitly flag that an atom leak is likely occurring and provide links to explanatory documentation. Similarly, if memory steadily climbs or request latencies degrade, the dashboard can isolate the top processes sorted by message queue length to pinpoint processing bottlenecks. McCord expresses deep gratitude to José Valim and the Erlang community members driving Telemetry forward, noting that pairing a standardized event interface with Phoenix Channels makes it trivial to deliver real-time operational insights.

```mermaid flowchart LR accTitle: Phoenix Telemetry VM Insights Flow accDescr: Metrics from the BEAM VM flow through Telemetry into Phoenix Channels to give developers automated bottleneck and memory heuristics. subgraph BEAM["Erlang BEAM Runtime"] AT["Atom Table Usage"] MQ["Process Message Queues"] end subgraph Tooling["Diagnostic Layer"] T["Telemetry Hooks"] H["Automated Heuristics<br/>(e.g., 90% Atom Limit)"] end subgraph UI["Phoenix Dashboard"] C["Phoenix Channels"] D["Real-Time Browser View"] end

AT --> T MQ --> T T --> H H --> C C --> D ```

- High BEAM atom table usage (e.g., above 90%) represents an unrecoverable memory leak pattern that heuristics can detect automatically. - Message queue inspection reveals concurrency bottlenecks where single processes fail to process incoming messages as quickly as they arrive. - The combination of a standardized telemetry contract and Phoenix Channels allows live VM instrumentation to be rendered in web pages without bespoke infrastructure.

[11:09](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=669s) The Origins of Phoenix: The Espresso-Bean-Fueled "Sync" Library

Before revealing his next major project, McCord asks the audience to withhold judgment until hearing the entire rationale, acknowledging that some might find the concept controversial or regressive at first glance. He traces the technical motivation back to 2013, when he was working primarily as a Ruby on Rails developer. During that era, the software industry was vigorously pushing client-side Single Page Applications (SPAs) as the only viable future, warning developers that continuing with server-rendered HTML meant becoming a "dinosaur." Loved by Rails for its developer productivity, McCord set out on a personal thought experiment to achieve real-time, dynamic web experiences without abandoning server-side development or taking on immense client-side JavaScript complexity.

The origin of that experiment involved an accidental caffeine overdose. McCord ate an entire bag of chocolate-covered espresso beans late one evening without realizing their potent caffeine content. Freaking out and unable to sleep, he stayed up all night coding; the result of that manic session was Sync, his first major open-source library, committed to GitHub on April 6, 2013. Sync was designed to render partial HTML templates on the server and push updates downstream to connected browsers via WebSockets without requiring custom client-side JavaScript or client CRUD logic. As McCord reflects on this pivotal moment in his career:

> “I stayed up all night and Sync is what fell out of that, I'm serious that's what happened, that's ultimately Phoenix probably wouldn't be here if I hadn't eaten that bag of espresso beans.”

Sync proved that the programming model of server-rendered real-time partials was compelling to developers, but the concurrency constraints of the Ruby ecosystem made production stability impossible. In order to operate, Sync required running a dedicated standalone WebSocket server alongside the primary Rails process. To broadcast updates without blocking Ruby's single operating system execution thread, McCord had to run an EventMachine event loop—essentially importing JavaScript's concurrency model into Ruby. In production, the EventMachine thread would silently terminate without alerting the host process, forcing McCord to write supervisor-like polling threads to detect dead background loops. Recognizing that Ruby's underlying platform would never allow this architecture to be robust or performant, McCord began scouting other languages. Six months after publishing Sync, he discovered Elixir, which possessed the native concurrency model required to realize his architectural ambitions and ultimately led to the creation of Phoenix.

[15:18](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=918s) Introducing Phoenix LiveView: The Quest for Rich UI Without SPA Fatigue

Five years after abandoning Sync, McCord announces that the Phoenix ecosystem has come full circle with a project tentatively named Phoenix LiveView. Developed outside Phoenix core as an independent, independently versioned package, LiveView builds directly upon Phoenix Channels to deliver rich, real-time user experiences that rival client-side JavaScript applications without incurring their systemic complexity. McCord anticipates skepticism from engineers who might accuse him of trying to build a “turbocharged horse buggy,” but he emphasizes that the modern JavaScript ecosystem imposes a disproportionate maintenance and cognitive burden on teams attempting to deliver ordinary interactive experiences.

McCord emphasizes that developers do not build heavy client-side applications simply out of a desire to use new technology; they do so because interactive interfaces directly impact business conversion rates. A standard multi-input registration form illustrates this dynamic:

1. **Step 1: User omission** — When confronted with a phone number field, the average user intentionally leaves it blank. In a classic server-rendered web application, clicking "Save" causes a disruptive full-page reload only to reveal that the field is mandatory. 2. **Step 2: Placeholder entry** — The user types an evasion like `"no thanks"` or `"N/A"` into the field and submits again, triggering another round-trip refresh and a subsequent validation error that the text is not a legitimate phone number. 3. **Step 3: Synthetic valid data** — The user enters a syntactically valid fake phone number and submits a third time. 4. **Step 4: Persistence collision** — The backend database validation runs and rejects the form because the entered email address is already registered to another account.

After four consecutive user rejections across three simple fields, conversion rates collapse. Teams turn to client-side frameworks to give instant inline feedback and eliminate submission friction. Yet implementing even this modest feedback loop in JavaScript forces teams into an exhausting labyrinth of tooling debates and library choices:

> “Just literally take an input tag and then maybe show another label error message beneath it, it's absolute insanity.”

Teams must choose between Ember, Angular, or React; negotiate whether to fetch data over REST or set up Apollo and GraphQL; configure compilation pipelines like Webpack or whatever bundler is newly fashionable; and reconcile disparate state models across server and client. LiveView is engineered to solve these core interactive needs directly from Elixir, eliminating the sprawling client-side stack while preserving immediate, interactive user feedback.

[18:57](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=1137s) The Front-End Complexity Trap vs. Most Real-World Apps

Chris McCord describes the escalating complexity that developers face when building rich front-ends. What starts as wanting a slightly dynamic user experience quickly turns into configuring loaders, Babel, and JSX for React. Then tutorials insist TypeScript is mandatory: suddenly developers find themselves learning an entirely new language and type system just to add an interactive feature around an HTML `<input>` tag they had already written. The cycle escalates further when business stakeholders note that a single-page app (SPA) hurt conversion rates because search engine optimization (SEO) suffered from client-side rendering. The common "fix" is adding a Node.js server between the client and the backend to render server-side HTML and rehydrate data in the browser. McCord characterizes this entire pipeline as excessive for what most products actually require.

```mermaid flowchart TD accTitle: Modern Front-End Complexity Creep accDescr: Diagram tracing the escalating requirements from a simple input tag to a multi-tiered Node SSR architecture.

subgraph ClientNeeds["Original Need"] Input["Static Input Tag"] --> UX["Add Dynamic Interaction"] end

subgraph Toolchain["Tooling Escalation"] UX --> Build["Webpack, Babel, JSX"] Build --> TS["Adopt TypeScript"] end

subgraph BusinessProblems["Architecture Band-Aids"] TS --> SEOLoss["Lost SEO & Conversions"] SEOLoss --> NodeSSR["Node.js Pre-rendering Server"] NodeSSR --> Rehydrate["Client Rehydration Layer"] end ```

McCord clarifies that he has worked on many client applications and is not arguing that JavaScript apps or client-side architectures are inherently bad. Some products—like Google Maps, Google Docs, or the Spotify Web Player where background audio cannot be interrupted across navigation—genuinely demand full client-side SPA architectures. However, the vast majority of applications built over the past decade fall somewhere between a plain HTML page and a desktop-like web app. Developers simply want lively, reactive features without maintaining an unwieldy ecosystem. McCord points out the broader cost: “There's so much complexity here to accomplish the rich user experience that we want and it may be justified in some cases but I think in a lot of cases if we had alternatives we would all be better off for it and better off in team size, team bandwidth, time to market, and just developer sanity.”

- Tooling requirements often spiral from JSX and Babel to entire language shifts like TypeScript just for small UI tweaks. - Server-side rendering (SSR) setups frequently require running a dedicated Node.js middle tier solely to rehydrate DOM state and satisfy SEO demands. - Dedicated client-side SPAs remain necessary for specific application classes (e.g., continuous audio streaming in Spotify, canvas-heavy tools like Google Maps), but represent an edge case rather than the standard web application.

---

[21:16](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=1276s) Porting the React Component Model to Phoenix LiveView

To illustrate how to bridge this gap, McCord analyzes React's classic "Hello World" clock example. At its base, React begins with a pure render function that takes data and returns markup—conceptually identical to calling `render/2` in a Phoenix view. What makes React powerful, however, is its stateful component model: components hold state on the client, and whenever that state changes, `render` is invoked automatically to update the UI. McCord explains that in 2013, looking at Elixir’s extreme rendering speed and the BEAM's native affinity for lightweight, stateful, evented server processes, the natural question was: why not move that exact model to the server?

```mermaid flowchart LR accTitle: React vs Phoenix LiveView State Loop accDescr: Comparison of how React manages state on the client versus LiveView managing state inside a BEAM process.

subgraph ReactModel["React (Client-Side)"] RState["Component State"] -->|"setState"| RRender["render()"] RRender --> RDOM["DOM Updates"] end

subgraph LiveViewModel["Phoenix LiveView (Server-Side)"] LVProcess["BEAM GenServer Process"] -->|"handle_info / handle_event"| LVRender["render/1 (EEx / ~L)"] LVRender -->|"WebSocket Diff"| BrowserDOM["Client Browser DOM"] end ```

He presents the React clock tutorial ported directly to Phoenix LiveView with zero omitted dependencies or hidden tricks. Using the `~L` sigil—a feature compiled to regular EEx templates inside Phoenix HTML—the LiveView view defines a pure `render/1` function. Underneath, a stateful BEAM process runs on the server. A recurring timer emits `:tick` via `Process.send_after` or `:timer.send_interval`. When received by `handle_info(:tick, socket)`, the process updates its socket state. Because the state changes, `render` runs automatically, and the resulting HTML is pushed down to the client. Running the demo live in Chrome, McCord inspects the page to highlight that while the server renders the template, a lightweight client diffing engine flashes only the updating `<h2>` element containing the clock.

- LiveView leverages the BEAM’s native strengths: lightweight concurrency, isolated stateful processes, and high-throughput rendering. - The `~L` sigil allows inline EEx templates that compile directly into standard Phoenix rendering structures. - Initial prototypes push the full rendered template HTML over the wire and run an in-browser DOM diff (with plan for fine-grained diffing optimizations in future iterations). - As McCord summarizes the approach: “The conceptual model is the same: the only difference is the UI updates has a little bit of a network hop to go over and a little bit of JavaScript running.”

---

[25:10](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=1510s) Client Interactions: Bindings, Event Handlers, and Validations

Pushing periodic server updates is only half the requirement; true rich web applications require interactive user input. McCord demonstrates Phoenix event bindings, which wire client-side interactions directly into the server process. Showing a minimal counter example, he jokes about having to compress the code onto a single slide by omitting `import` statements and collapsing a `use` call into a single line, which provoked a humorous rebuke from José Valim for violating the Elixir code formatter.

In the template, adding client reactivity is as simple as adding standard HTML attributes, such as `phx-click="decrement"` or `phx-click="increment"`. When clicked, the browser dispatches that event name across the WebSocket directly into a `handle_event/3` callback inside the LiveView process. If `handle_event/3` mutates the socket's state, LiveView automatically re-renders the template and pushes the changes back to the client; if the handler leaves state unchanged, no render takes place.

```mermaid flowchart LR accTitle: Phoenix LiveView Event Interaction Cycle accDescr: Flow diagram showing user interaction traveling from client DOM to server handle_event and back to browser DOM.

Browser["Browser: Button Click"] -->|"phx-click event over WS"| HandleEvent["Server: handle_event/3"] HandleEvent -->|"Mutate socket.assigns"| ReRender["Server: render/1"] ReRender -->|"HTML Patch / Diff"| DOMPatch["Browser: Targeted DOM Patch"] ```

McCord demonstrates the counter running live in the browser at `/counter`. Clicking the increment and decrement buttons alters the state instantly inside the underlying GenServer without writing a line of custom client-side JavaScript. He then shows how this pattern solves larger real-world challenges, such as live form validations. Instead of wiring up a client validation framework, an engineer can place a `phx-change="validate"` binding on an existing `form_for` tag. Because `form_for` treats `phx-change` as an arbitrary data attribute, the form needs no custom scaffolding: whenever any input field triggers a change, the client serializes the form data, passes it to the LiveView `handle_event("validate", %{"user" => params}, socket)` clause on the server, runs standard Ecto changeset validations, and reflects inline validation errors immediately back to the user interface.

- Interactive bindings use declarative HTML data attributes like `phx-click` and `phx-change`. - LiveView runs on top of standard Elixir processes: “This thing could be doing anything, it's just a GenServer underneath; it's holding state, it's reacting to events.” - Form validation workflows map directly onto idiomatic Phoenix controller idioms, turning an empty changeset initialized during `mount`/`init` into dynamic, per-keystroke Ecto validations.

[29:00](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=1740s) Server-Side Validations and `phx-submit`

Chris McCord explains that instead of the standard controller workflow—evaluating whether an Ecto changeset is valid and choosing which template to render—a LiveView simply places the updated changeset directly into its state. If the state has changed, the template automatically re-renders and the diff is pushed to the client, providing immediate, instant feedback. This eliminates the massive complexity typically required for client-side validations, especially when internationalization comes into play. Shipping Gettext translations to the browser requires deciding on a JSON payload shape, serializing the data, and sending substantial payloads over the wire just to translate validation messages. With LiveView, all of that localized validation logic stays on the server with zero extra effort: “literally this is the only amount of code required to get client-side style validations.”

When persisting data, LiveView introduces another binding called `phx-submit`. Rather than submitting an HTML form via a standard POST request to a traditional controller, the form submits directly back to the active LiveView process. McCord notes that while submitting to a controller is still possible, round-tripping through a separate controller action just to re-render in an error condition wastes work when a stateful process is already running. Handling persistence simply requires adding `phx-submit="save"` and defining a `handle_event("save",...)` callback. While noting that the API is still evolving as they blend controller semantics with stateful paradigms, McCord highlights the productivity gains: “now suddenly the application is ten times more interactive and feature full with potentially less code.”

On a successful save, the callback sets a flash message and redirects directly to another LiveView rather than routing through a controller, allowing traditional controllers to fall away entirely. In the error condition, the model becomes even simpler than standard controller code: instead of branching to manually render an `:edit` or `:new` template with assigned variables, the callback merely updates the socket state with the invalid changeset. Because the template is already rendering against that state, the view updates reactively. McCord envisions a generator like `mix phx.gen.live_view` that produces a fully real-time application in fewer lines of code than the standard `mix phx.gen.html` scaffolding.

```mermaid flowchart TD accTitle: LiveView Form Submission Flow accDescr: Diagram illustrating how phx-change and phx-submit events are handled within the LiveView process instead of routing to an external controller.

subgraph Client["Client Browser"] Input["User Input / Keypress"] Submit["Form Submission"] end

subgraph LiveView["LiveView Process"] Validate["handle_event('validate')<br/>Update Changeset in State"] Save["handle_event('save')<br/>Persist Resource"] Check{"Valid?"} PushDiff["Push Minimal Diff"] Redirect["LiveView Redirect"] end

Input -->|"phx-change"| Validate Validate --> PushDiff PushDiff -->|"DOM Patch"| Client

Submit -->|"phx-submit"| Save Save --> Check Check -->|"Failure"| Validate Check -->|"Success"| Redirect ```

---

[31:53](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=1913s) Live Form Validation and Cross-Tab Real-Time Sync

Moving to a live demonstration, McCord opens a user registration screen (`/users/new`). The rendered output is clean, standard HTML delivered on the initial HTTP request, backed by a persistent server process listening for validation events over a WebSocket connection. Because browser input fields emit change events on keypress, typing triggers instantaneous server-side validation. Typing the name "José" produces an immediate inline error—"must be letters and numbers"—which updates in real time without refreshing the page. McCord recalls demonstrating the feature across continents: “and it was like indistinguishable from a local JavaScript application even from my laptop Wi-Fi away Nick hop to José.” As soon as an email address or phone number becomes valid, the error indicator vanishes without extra client-side tooling.

McCord demonstrates cross-client reactivity by opening a second browser tab displaying a full CRUD user index view generated with standard EEx. When he saves the new user in the registration form, the user immediately registers, a flash message appears, and the user index view in the adjacent tab dynamically updates with the new record.

He then navigates to an edit screen to change the user's name from "José" to "José Valim." Saving the edit updates the user list in the adjacent browser tab instantly. To test deletion, McCord jokes about José trolling him too much and needing a "perma-ban," and clicks delete. The open user page immediately redirects while displaying a flash message stating the user has been removed, demonstrating coordinated real-time state across views with zero custom JavaScript.

---

[35:05](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=2105s) Real-Time Updates via Phoenix PubSub and Contexts

McCord reveals the implementation behind the live show view, showing that the entire real-time experience requires fewer than twenty lines of code. The view subscribes via Phoenix PubSub through an `Accounts` context to receive notifications whenever a user is modified or deleted.

```elixir def handle_info({:user_updated, user}, socket) do {:noreply, assign(socket,:user, Accounts.get_user!(user.id))} end

def handle_info({:user_deleted, _user}, socket) do {:noreply, socket |> put_flash(:info, "User deleted.") |> redirect(to: Routes.live_path(socket, UserIndexView))} end ```

Whenever the view receives a `:user_updated` message, it handles the event by refreshing the socket data. While broadcasting updated payloads directly is viable, the demo naively refetches from the database. McCord points out the architectural implications: if thousands of concurrent users view the same record, refetching on every update could quickly overwhelm the database. He mentions ongoing discussions with Ben Wilson from the Absinthe project about applying DataLoader batching techniques and Absinthe subscription patterns to mitigate query load in LiveView architectures. When a `:user_deleted` message arrives, the process sets a flash message and redirects, terminating the view:

> “the model of this real-time application is like I said simpler than for me then the actual CRUD HTML that we generate it's like fewer files fewer conceptual leaps and you get this real-time experience that is insane in my opinion”

---

[36:47](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=2207s) Exploring Boundaries and Phoenix Presence Without JavaScript

McCord addresses the boundaries and constraints of server-driven UI, acknowledging that the Phoenix team is still discovering where the paradigm works best and where it reaches its limits. LiveView excels at scoped, rich UI interactions: real-time form validation, background job progress indicators, and live operational dashboards receiving server-pushed stats. For full-blown client applications like complex chat systems, McCord remains cautious, stating that they need to explore the architecture further before declaring it a complete solution for high-frequency client state. The team's approach is to start with simpler use cases that the server can solve cleanly, identifying exactly when developers should bite the bullet and adopt client-side frameworks, though McCord believes: “I think we can go a lot further than most people think.”

To demonstrate the extent of what is possible, McCord revisits Phoenix Presence. In Phoenix 1.3, the Presence JavaScript API was purely functional, which inadvertently introduced race conditions when client state shifted mid-sync. Phoenix later revised the JavaScript client to a stateful API, significantly reducing client boilerplate and resolving the timing issue. However, LiveView eliminates the client code altogether. By backing the LiveView with Phoenix Presence on the server, active users appear and disappear from the page in real time across browser tabs with zero custom JavaScript written by the developer.

- **Primary LiveView sweet spots:** Inline form validation, multi-step wizards, background task progress meters, and dynamic server-driven dashboards. - **Under evaluation:** High-frequency, heavy-state interactions like full-scale chat interfaces and highly asynchronous offline-first clients. - **Phoenix Presence integration:** Eliminates custom client tracking code by maintaining presence collections entirely within the server-side LiveView process.

[38:59](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=2339s) Live Presence Tracking Without Client-Side Code

Chris McCord demonstrates a real-time presence tracking feature built entirely without JavaScript. In the template, the code simply iterates over a list of users with a standard `for user <- @users` comprehension. When he opens a second browser tab, the UI instantaneously reflects that the user is now connected across two tabs. Closing one tab drops the count back to one, and closing that tab removes the user from the screen entirely. The interface responds with the same immediacy expected from a dedicated client-side JavaScript application, yet every change is driven by server-side rendering over standard Phoenix Channels subscriptions.

```mermaid flowchart LR accTitle: Phoenix Presence Live View Data Flow accDescr: Browser tabs connect to the LiveView process, which subscribes to Presence topics and diffs state to the DOM. subgraph Clients["Browser Clients"] T1["Tab 1"] T2["Tab 2"] end subgraph Server["Server Runtime"] LV["Phoenix LiveView Process"] PT["Phoenix.Tracker / Presence"] end T1 <-->|"WebSocket"| LV T2 <-->|"WebSocket"| LV LV <-->|"track & subscribe"| PT PT -->|"presence_diff event"| LV LV -->|"Morphdom HTML patch"| T1 ```

McCord candidly admits to cutting corners in this early prototype. Phoenix Tracker and Phoenix Presence were originally architected around client-side libraries that reconcile replicated presence events directly in JavaScript. Because an equivalent client for pure server-rendered Elixir state had never been necessary before LiveView, McCord took a temporary shortcut: on any presence state change, the server simply refetches and re-renders the complete list of presences. While this unoptimized approach is unsuitable for high-volume production deployments, he confirms the team will build a dedicated client abstraction to handle presence event stitching cleanly on the BEAM.

> “So in a few lines of server code I have Phoenix Presence features on a server rendered application which is kind of ridiculous.”

- Demonstrates instant multi-device presence tracking with zero custom JavaScript written. - Initial implementation naively refreshes the full presence list on each state change because no Elixir-side data-stitching client existed previously. - Underneath, LiveView leverages `Phoenix.Tracker` and standard PubSub subscriptions.

---

[40:41](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=2441s) The Absurd 60 FPS Rainbow Animation Stress Test

Addressing the inevitable question of whether server-side rendering can realistically scale, McCord notes that rendering on the server will inherently burn more CPU cycles than offloading presentation logic to the user's browser. However, the performance characteristics of Elixir and the BEAM make the trade-off viable. To prove just how fast server rendering can be, he presents a stress-test demo conceived with José Valim.

While researching client-side DOM diffing tools, Valim came across Marko.js—a UI library written by the authors of morphdom. Marko’s marketing demo showcased a silky-smooth, 60-frame-per-second rainbow animation that did not rely on CSS animations or hardware acceleration. Instead, it continuously re-rendered 85 `<div>` tags at 60 FPS, calculating hue, rotation, and transform styles purely in JavaScript templates. Valim challenged McCord to port the entire benchmark into an Elixir LiveView.

```mermaid flowchart TD accTitle: 60 FPS Server Render Loop accDescr: A recursive 16ms timer triggers LiveView re-renders that stream JSON-encoded HTML diffs to the browser. Timer["Process Timer (:timer.send_interval 16ms)"] -->|"tick"| LV["LiveView Process"] LV -->|"Ported math calculations"| State["Update hue & CSS transform"] State -->|"render/1"| HTML["85 <div> tags generated"] HTML -->|"JSON over WebSocket"| Browser["Client (morphdom diff & patch)"] Browser -->|"phoenix-click"| LV ```

McCord ported the math and trigonometry routines directly into Elixir and configured a server process to send itself an `:update` message every 16.6 milliseconds using `:timer.send_interval`. On every tick, the LiveView updates its internal state, re-renders all 85 dynamic elements, and pushes the diff across the WebSocket to the browser. To their astonishment, the animation rendered smoothly at 60 FPS directly from the server. Furthermore, adding interactivity—reversing the rotation direction via a `phx-click="switch"` binding—required only three additional lines of code to multiply the animation step by `-1`.

> “I’m not saying use this for animations, but can it scale? Yes, it can scale, like it’s absurd, but that this worked.”

- Ported directly from a Marko.js marketing benchmark built by the creators of morphdom. - The server computes math and style transforms across 85 dynamic `<div>` tags every 16.6 milliseconds. - Currently unoptimized: converts I/O data to an intermediate binary before JSON serialization, leaving significant room for performance improvements. - Reversing animation direction dynamically on user click required only three lines of Elixir.

---

[45:35](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=2735s) Trade-offs, Pragmatism, and Productivity

While optimistic about LiveView's potential, McCord tempers expectations and emphasizes that server-driven UI is not a universal replacement for JavaScript frameworks. Single-page application (SPA) architectures remain essential for applications requiring offline capability, desktop-like native device integrations, or complex interfaces that demand zero-latency optimistic UI updates. High-latency edge connections will inherently feel slower if every minor micro-interaction must traverse the network back to the BEAM.

```mermaid flowchart TD accTitle: Architecture Selection Guide accDescr: Decision tree outlining when to choose client-side JavaScript frameworks versus Phoenix LiveView. Decision{"App Requirements?"} Decision -->|"Offline support / zero-latency optimistic UI"| Client["Client-Side SPA (React, Vue, Ember)"] Decision -->|"Standard CRUD / Real-time forms / Rapid time-to-market"| Live["Phoenix LiveView"] Live --> Win1["~60 lines of client JS"] Live --> Win2["Native SEO without hydration"] Live --> Win3["Seconds to build vs days"] ```

At the same time, McCord argues that the vast majority of web applications operate on standard client-server patterns where users naturally submit forms and await responses. In these scenarios, LiveView offers unmatched developer velocity. Form validations that might take hours or days to wire up across client-side state managers, REST endpoints, and schema validators can be built in thirty seconds using standard Elixir code. Additionally, shipping roughly 60 lines of client-side integration JavaScript avoids multi-megabyte bundle downloads, delivering near-instant initial page loads and effortless search engine optimization out of the box.

> “If you want like great productivity or a great time to market, I think we’re going to be unbeatable in that regard.”

- Not suitable for offline-first applications or environments demanding client-side optimistic UI updates. - Radically improves time to market by eliminating redundant client/server state orchestration and schema duplication. - Extremely lightweight client footprint: initial prototype required only ~60 lines of client-side glue JavaScript. - Automatic SEO compatibility, as the initial request returns standard server-rendered HTML before establishing the WebSocket connection.

---

[48:18](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=2898s) Rendering in Microseconds

McCord highlights that LiveView is made practical by Phoenix's ability to render templates in microseconds rather than milliseconds. He credits core team member Jason Stiebs for introducing the microsecond (`µs`) unit into Phoenix's console logging. Prior to that change, rendering durations would display as awkward fractional milliseconds like `0.4ms`.

Displaying render times explicitly in microseconds became a standout marketing asset for the framework, consistently astonishing developers outside the ecosystem and underscoring the raw rendering speed that enables LiveView to push continuous server diffs without degrading system performance.

> “Jason on the Phoenix core team was the one that actually added the microsecond character... and I think this is like the gift that keeps on giving, because people see this like, ‘What’s this micro character?’ and they’re like, ‘Oh my god.’”

- Phoenix template rendering times are measured and logged in microseconds (`µs`). - Introduced by Jason Stiebs to replace awkward sub-millisecond representations like `0.4ms`. - Sub-millisecond execution speeds serve as the foundational bedrock enabling real-time server diff streaming.

[48:59](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=2939s) Performance Beyond Bragging Rights and Advancing the State of the Art

Chris McCord tackles the skepticism surrounding microsecond-level response times, acknowledging the counterargument that end users cannot perceive the difference between a page responding in 40 milliseconds versus 400 microseconds. While he admits that raw speed can serve as a convenient marketing badge, he insists it has to be about something deeper than mere boasting. Reflecting on a blog post that compared web framework performance—specifically Ruby versus Elixir—McCord notes that he actually agreed with their premise questioning whether a web application could simply be "fast enough" for the types of apps built over the past decade. If developers are merely building traditional applications, user-perceived speed differences at that scale are negligible.

However, McCord sharply distinguishes between legacy paradigms and pushing the frontier forward. When choosing a programming language or web framework, high performance and low overhead become pivotal because they unlock entirely new architectural models that were previously impractical. By leveraging a high-performance, concurrent platform, development teams can deliver richer real-time features, iterate faster than market competitors, and expand developer productivity. McCord summarizes this imperative directly: “If we're trying to build applications that we've always built then it doesn't matter, but if we want to advance the state of the art, if we want to beat other competitors to market, produce features faster than them, then absolutely the choice of framework based on performance is critical.”

```mermaid flowchart TD accTitle: Performance Impact on Web Development Paradigms accDescr: Contrasts traditional web applications with advanced real-time platforms.

subgraph Legacy["Traditional Web Paradigms"] direction TB A["Standard Web Request"] -->|"40 ms vs 400 µs"| B["Negligible User Impact"] B --> C["Performance Treated as Mere Bragging Rights"] end

subgraph Modern["Next-Gen State of the Art"] direction TB D["High-Throughput Elixir / BEAM Core"] --> E["Eliminates Architectural Overhead"] E --> F["Enables Rich Real-Time Models (LiveView)"] F --> G["Faster Time-to-Market & Competitive Edge"] end ```

[50:56](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=3056s) "The Future Is Yours to Build"

Building on José Valim's opening keynote remark that "the future is in your hands" regarding Elixir adoption and core evolution, McCord adds his own rallying cry: the future is yours to build. With the BEAM and Phoenix, developers have an environment capable of achieving things fundamentally out of reach for other runtimes. He challenges the audience to reject the status quo of modern web development and consider what is now possible across the entire spectrum of Elixir tooling.

As a primary example, McCord highlights Nerves and IoT deployments. Referencing an earlier presentation by Justin Schneck, he describes how connected hardware devices running in the field communicate back to Phoenix servers over Phoenix Channels, updating their firmware on the fly simply by receiving a backend message: “Justin gave uses Phoenix Channels on the server, and it completely blew my mind that we were able to have devices just update their firmware based on getting a message from a server on the backend.” Expanding into these diverse environments proves that the BEAM ecosystem is poised to disrupt multiple markets if the community actively embraces building ambitious software.

[52:16](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=3136s) Q&A: Client vs. Server State and Durability in LiveView

Opening the floor to questions, McCord fields an inquiry regarding client-side state handling and durability, using a multi-step form wizard as an example. When a user progresses through steps, the questioner asks what happens if a network interruption occurs, the server process crashes, or the user leaves the page. McCord points out that in LiveView, state is kept strictly on the server. To avoid race conditions and complex debouncing issues caused by network latency, event bindings like `phx-change` and `phx-submit` serialize and transmit the whole form rather than just a single delta value.

Regarding state durability, McCord notes that there is no magic mechanism built directly into LiveView; rather, each connection is an ordinary, stateful Elixir process. Developers can apply the same persistence patterns they would use in standard controller-based architectures or GenServers. For long-running flows like wizards, developers can: - Persist the serialized form state to the database on every valid step advance (e.g., clicking "Next"). - Periodically dump process state to a datastore every few seconds. - Rehydrate the state when the user returns or reconnects, mirroring conventional transactional endpoints.

[54:54](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=3294s) Q&A: Broadcast Coalescing, Release Timeline, and Multi-Node Deployments

The discussion shifts to performance optimization when many clients view identical dynamic content simultaneously. An audience member asks whether LiveView can coalesce rendering when hundreds of users view the exact same HTML fragment. McCord acknowledges that in early iterations, each client renders independently, but points to Phoenix Channels' existing "fast-laning" architecture as a proven blueprint. In standard Phoenix Channels, JSON payloads broadcast to thousands of connected chat users are encoded a single time rather than once per recipient, unless explicitly intercepted for customization. A comparable optimization pattern is planned for template fragments in LiveView.

An attendee then asks when LiveView will be publicly available. McCord replies that it will arrive soon because it is maintained outside of Phoenix core, allowing him to push early `0.0.1` preview releases and iterate rapidly without waiting on Phoenix framework release cycles. He jokingly cautions the audience: “I need to get Phoenix 1.4 out first, so don't let me focus on something else and then I can just claim like, 'Oh, well I promised it would be out in two weeks so I can't release Phoenix.'” He cautions that early adopters should expect significant changes to the public API and interfaces.

```mermaid flowchart LR accTitle: Distributed Multi-Node LiveView Architecture accDescr: Shows how a load balancer, distributed Erlang, and Phoenix PubSub route a client WebSocket to a server PID.

User["Client Browser"] -->|"WebSocket Connection"| LB["Load Balancer"] LB --> NodeA["Phoenix Node A"]

subgraph Cluster["Distributed Erlang Cluster"] NodeA <-->|"Phoenix PubSub"| NodeB["Phoenix Node B (LiveView PID)"] end

User -.->|"Serialized PID Token"| NodeB ```

Addressing a final technical question about routing sessions across multiple servers behind a load balancer, McCord explains that LiveView works seamlessly across clusters. Because client connections use WebSockets, load balancers maintain sticky connections per socket. Furthermore, Phoenix PubSub is inherently distributed across BEAM nodes, and the client holds a serialized token referencing its corresponding process identifier (`pid`) on the server. If distributed Erlang is configured across the cluster, cross-node communication operates automatically without requiring custom session stickiness hacks from developers.

McCord wraps up the session by clarifying a conference discount code question (noting that the code applies to The Pragmatic Bookshelf site rather than Amazon), directs attendees to find him for follow-up questions, and hands the stage over to conference organizer Jim Freeze.

## Mentioned Chris McCord, José Valim, Jason Stiebs, Justin Schneck, Ben Wilson, Jim Freeze, DockYard, Plataformatec, Erlang Solutions, The Pragmatic Bookshelf, Phoenix, Phoenix LiveView, Phoenix Channels, Phoenix Presence, Phoenix PubSub, Telemetry, Ecto, Plug, Absinthe, Nerves, Sync, React, Marko.js, morphdom, Ember, Angular, TypeScript, Webpack, Babel, Ruby on Rails, StatsD, Prometheus, New Relic, Programming Phoenix

[Source](https://www.youtube.com/watch?v=Z2DU0qLfPIY)

中文简报

McCord 发布 Phoenix LiveView,向 JavaScript 单页应用发起挑战 通过 WebSocket 流式传输实时模板差异,BEAM 上的服务端有状态渲染在消除复杂 JavaScript 框架的同时,实现了媲美客户端应用的交互性。 Chris McCord 首先介绍了生态系统的里程碑,详细说明了 Phoenix 1.4 的即将完成、官方《Programming Phoenix》图书的更新,以及用于统一指标的最新协作项目 Telemetry。随后,他展示了本次主题演讲的核心内容:Phoenix LiveView。这是一个服务端驱动的 UI 库,旨在通过 WebSocket 提供响应迅速的实时用户体验,同时摆脱现代客户端 JavaScript 单页应用的运维开销与工具链疲劳。McCord 通过实时表单验证、多标签页在线状态追踪以及直接在 BEAM 上渲染的令人惊叹的 60 FPS 动画展示了 LiveView 的能力,随后概述了其架构权衡、性能优势和多节点集群策略。 Phoenix 1.4 即将发布,带来原生 HTTP/2 支持、更快的编译速度、展示路由列表的 404 页面,以及由 José Valim 重构、旨在简化自定义 WebSocket 实现的模块化传输层。 (0:00) 与 Erlang Solutions 和 Plataformatec 合作打造的新开源 Telemetry 库,引入了无预设偏见、不含宏的核心调度 API,旨在统一整个生态系统的指标收集。 (4:49) Chris McCord 正式发布 Phoenix LiveView,这是一个运行在 WebSocket 之上的服务端渲染实时 UI 框架,利用标准的 Elixir GenServer 即可提供丰富的交互性,而无需专用的客户端 JavaScript 框架。 (15:18) 移植自 Marko.js 的跨 85 个动态元素的 60 FPS 彩虹动画压力测试表明,BEAM 进程和微秒级模板渲染能够大规模承载极端的服务端驱动 DOM 更新。 (40:41)

Reading summary

Chris McCord argues that modern web development has incurred unsustainable tooling complexity to achieve dynamic user interfaces, presenting Phoenix LiveView alongside Phoenix 1.4 and the Telemetry project to demonstrate that high-throughput server-rendered architectures can deliver rich interactivity with minimal client-side code. Phoenix 1.4 and Standardized Ecosystem Telemetry (4:49) Phoenix 1.4 introduces native HTTP/2 support, faster compilation times, an interactive router listing on development 404 pages, and a refactored transport layer that simplifies custom WebSocket implementations while resolving a race condition in the JavaScript Presence library. Alongside framework updates, the ecosystem launched Telemetry, an open-source collaboration with Erlang Solutions and Plataformatec. Telemetry addresses metrics fragmentation through a minimal, macro-free core using `telemetry:execute/3` and ETS lookups that act as virtually zero-cost no-ops when handlers are unattached. Decoupling event emission from reporting packages like StatsD and Prometheus enables libraries such as Ecto and Plug to emit standardized data without breaking API changes. McCord explains that this shared instrumentation foundation enables automated browser-based diagnostics, allowing Phoenix tools to translate runtime statistics—such as an atom table reaching 90% capacity or accumulating process message queues—into direct operational guidance for teams lacking deep BEAM expertise. SPA Tooling Fatigue and the LiveView Alternative (15:18) McCord traces LiveView's conceptual roots to his 2013 Ruby library Sync, which rendered real-time server partials over WebSockets but proved unsustainable due to Ruby's single-threaded concurrency constraints and fragile EventMachine background loops. While acknowledging that specialized applications like Google Maps or continuous audio platforms like Spotify genuinely require client-side single-page applications (SPAs), McCord contends that standard business applications adopted SPAs primarily to improve conversion rates via responsive user feedback, such as inline form validations. However, implementing basic client-side interactivity has saddled teams with extensive tooling overhead, including complex bundler configurations, alternative type systems, client state stores, and intermediate Node.js servers deployed solely to reconcile server-side rendering for search engine optimization. LiveView was developed outside Phoenix core to restore developer velocity, allowing engineers to build rich, reactive user flows entirely within Elixir while avoiding sprawling client-side toolchains. Server-Driven Component Cycles and Reactive Workflows (21:16) LiveView adapts React's stateful component model to the server, pairing lightweight BEAM processes with pure `render/1` functions powered by the `~L` EEx template sigil. Declarative HTML attributes such as `phx-click`, `phx-change`, and `phx-submit` bridge user input directly across WebSockets to server-side `handle_event/3` callbacks. When a callback mutates socket state, LiveView automatically re-renders the template and pushes compact HTML diffs back to the client DOM via morphdom. This workflow eliminates redundant client-side orchestration. Form inputs serialize directly into standard Ecto changesets on keystroke, delivering localized validation errors without dedicated JSON schemas or client-side translation files. Furthermore, views integrate with Phoenix PubSub and Phoenix Presence on the server, synchronizing database changes and multi-tab user presence tracking across clients with zero custom JavaScript written by the developer. Microsecond Performance, Distributed Clusters, and Boundaries (40:41) To demonstrate the runtime limits of server-rendered interfaces, McCord presents a ported Marko.js benchmark rendering a 60-frame-per-second rainbow animation that recalculates trigonometric styles across 85 dynamic elements every 16.6 milliseconds over WebSockets without dropped frames. Sub-millisecond and microsecond template rendering speeds enable this throughput, though McCord emphasizes that LiveView is not a universal solution; applications requiring offline operation, client-side optimistic UI updates, or resilience across high-latency edge connections still require client-side architectures. In clustered production deployments, LiveView processes maintain connection affinity through sticky WebSocket routing at the load balancer, while distributed Erlang and Phoenix PubSub handle multi-node coordination via serialized process identifiers. State durability follows standard OTP design principles, requiring developers to persist wizard progress or transactional steps directly to databases rather than relying on ephemeral socket memory. LiveView reclaims server-side development velocity by pairing BEAM-backed concurrency with microsecond template rendering to deliver interactive interfaces with roughly 60 lines of client glue code. While McCord notes that high-frequency client state and offline applications still demand dedicated front-end tooling, he frames server-driven architectures as an optimal, low-overhead default for the majority of modern web applications.

精炼阅读摘要

Chris McCord 在演讲中介绍了 Phoenix 1.4 的更新细节与统一指标项目 Telemetry,并首次公开了 Phoenix LiveView,主张利用 BEAM 原生的并发与微秒级渲染能力在服务端驱动富交互界面,以替代大多数常规 Web 应用中过度复杂的单页应用(SPA)前端架构。 Phoenix 1.4 与 Telemetry 生态指标体系 (0:00) Phoenix 1.4 引入了 HTTP/2 原生支持、更快的开发编译速度、展示所有路由的 404 调试页面,并修复了 JavaScript Presence 客户端库中的时序竞争问题。由 José Valim 主导重构的传输层提取了模块化组件,使开发者扩展 WebSocket 传输时无需再完整复制底层代码。此外,针对以往 Elixir 社区指标收集方式零散、缺乏统一标准的问题,Phoenix 团队联合 Erlang Solutions 与 Plataformatec 推出了独立的开源指标核心库 Telemetry。Telemetry 放弃了宏编程,采用极简的调度接口,在无监听回调时仅执行低开销的 ETS 查找;上层系统可通过 Phoenix Channels 监听如原子表占用超过 90% 或进程消息队列堆积等异常状态,将 BEAM 运行时的关键诊断自动转化为直观的浏览器监控看板。 前端复杂性困境与 LiveView 的架构构想 (11:09) McCord 回顾了 2013 年在 Ruby 社区开发 Sync 库的经历,当时尝试通过服务端推送局部模板避免 SPA 方案,却受制于 Ruby 单线程与 EventMachine 脆弱的并发模型,促使他转向 Elixir 并创立 Phoenix。他指出,现代前端工程为了实现表单即时验证等基础交互,常常迫使团队引入繁杂的构建流水线、TypeScript、状态管理库,甚至为了搜索引擎优化(SEO)和首屏转化率额外架设 Node.js 服务端渲染(SSR)层并执行客户端注水。尽管特定重交互应用(如 Google Docs、Google Maps 或需无缝音频播放的 Spotify)仍必须依赖客户端 SPA,但大量常规 Web 业务的需求介于静态页面与桌面级程序之间。Phoenix LiveView 依托 Phoenix Channels,将类似 React 的状态组件模型迁移至服务端的 BEAM 进程中,通过维护局部状态驱动渲染,以极小的客户端胶水代码解决绝大部分交互需求。 声明式事件绑定与服务端驱动的表单状态 (25:10) LiveView 采用 ~L 标识符编写内联 EEx 模板,并基于标准 HTML 属性扩展交互,例如通过 phx-click 派发点击事件,或通过 phx-change 与 phx-submit 绑定表单校验和持久化逻辑。客户端捕获浏览器输入后,通过已建立的 WebSocket 连接将整个表单参数序列化发送给服务端 GenServer 进程的 handle_event/3 回调;若状态发生改变,服务端自动执行 render/1 并向下推送 HTML 差异,浏览器端仅运行轻量 DOM 补丁库更新变化节点。该模式让 Ecto 变更集(changeset)的验证逻辑完全留在后端,省去了前后端重复编写字段约束校验以及将国际化文本序列化为 JSON 传输的额外成本;当数据通过 phx-submit 验证保存后,进程可直接设置闪存提示并跳转至目标 LiveView,削减了传统控制器与客户端状态同步的代码层级。 微秒级服务端性能与 60 帧极限压力测试 (40:41) 为验证服务端驱动界面的扩展能力,McCord 与 Valim 将 Marko.js 的彩虹动画基准移植至 Elixir:服务端启动递归定时器,每隔 16.6 毫秒向自身发送更新消息,实时计算 85 个 <div> 标签的色相与 CSS 变换矩阵并推送到浏览器,在前端 DOM 补丁机制下实现了流畅的 60 FPS 动画效果。这一特性的基础在于 Phoenix 模板渲染时间仅为微秒(µs)级。McCord 强调,微秒级执行速度并不仅是数字比拼,它根本性地改变了系统架构的可行性边界,使得原本在传统服务端框架中不可想象的高频 diff 推送在单台服务器上具备实用性。同时,LiveView 首屏返回标准 HTML,在天然保障 SEO 友好的同时将客户端 JavaScript 依赖压缩至数十行级别。 技术边界、集群分布与架构权衡 (45:35) McCord 强调 LiveView 并非旨在全盘取代客户端框架,其最适用的甜点区包括行内表单校验、多步向导、后台任务进度跟踪及实时运维看板;离线优先模式或要求绝对零延迟乐观 UI 的复杂场景仍需采用传统前端 SPA。在状态持久性方面,LiveView 依赖普通 Elixir 进程维持会话,向导类复杂流程仍需主动将中间状态写入数据库以应对进程崩溃或重连。针对多节点部署,客户端 WebSocket 连接可经由负载均衡器维持长连接,而底层的 Phoenix PubSub 会依托分布式 Erlang 在集群节点间透明路由消息。针对高并发下多客户端订阅相同内容的渲染消耗,McCord 预计将借鉴 Phoenix Channels 的快速通道(fast-laning)广播机制,对公共模板片段执行单次编码优化。 McCord 总结指出,框架极致的性能与并发优势是为了拓展 Web 架构的前沿,使开发者能以更少的代码与更短的上线时间交付高响应系统。当前 LiveView 仍处于独立快速迭代阶段,团队在正式发布前将优先完成 Phoenix 1.4 的发布并重构底层追踪抽象,其长期适用边界与广播批处理机制仍有待在社区生产实践中进一步检验。

Detailed notes — zh

## Background 该视频由 ElixirConf 官方频道于 2018 年 9 月 8 日发布在 YouTube 平台上,记录了 Chris McCord 在 ElixirConf 2018 上的主题演讲。虽然原页面未提供详细简介,但视频完整呈现了关于 Phoenix 1.4 进展及全新技术特性的分享。 ## TL;DR Chris McCord 首先回顾了即将完工的 Phoenix 1.4 新特性、传输层重构以及生态级指标规范 Telemetry 的诞生。随后,他通过当年浓缩咖啡豆引发的 Ruby Sync 项目经历,阐述了转向 BEAM 平台的并发动因,并正式发布了革命性的 Phoenix LiveView。演讲深度剖析了 LiveView 如何利用服务端进程持有状态、结合轻量 DOM 差异比对来消除前端 SPA 的沉重复杂度,并通过实时表单校验、跨洋联动以及震撼全场的 60 FPS 动画 Demo,证明了该模式卓越的开发生产力与性能边界。 ## Atmosphere & style 整场演讲充满激情、幽默风趣且节奏明快,Chris 善于用极具画面感的自嘲(如吃光整包咖啡豆通宵写代码、调侃故意留着 issue 不关)拉近与观众的距离。现场演示接连打破技术常规,从零延迟表单联动到硬核的 60 FPS 服务端彩虹动画,引得台下掌声与惊呼不断,技术自信与感染力拉满。整体氛围既像是一场向过度前端复杂性宣战的开发者布道,又洋溢着对 Elixir 和 BEAM 平台无限潜能的极客自豪感。 ## Bulletin - [15:07](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=907s) Chris McCord 正式公布了 Phoenix LiveView,该项目旨在通过服务端长连接与 DOM 局部 diff 机制,无需编写繁重的前端 JavaScript 即可构建具备富交互能力的 Web 界面。 - [40:41](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=2441s) 现场演示展示了由纯 Elixir 服务端通过定时器与 WebSocket 驱动的 60 FPS 动画,证明了 BEAM 虚拟机与 Phoenix 在极端高频计算和实时推送场景下的性能裕量。 - [00:00](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=0s) Phoenix 1.4 正式版已基本完成,带来了 HTTP/2 支持、更快的开发模式编译速度、重构后的 Transport 层,以及修复了竞态条件的全新 Presence 客户端。 - [04:49](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=289s) Erlang Solutions 与 Plataformatec 联合推出了统一的指标规范库 Telemetry,为 Phoenix、Ecto 及整个 BEAM 生态提供极低开销、可插拔的可观测性底层接口。 ## Key takeaways - [00:00](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=0s) Phoenix 1.4 核心代码已收尾,重点转向完善配套文档和书籍,更新包括 HTTP/2、编译加速及更轻量的 WebSocket Transport 层。 - [04:49](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=289s) Telemetry 作为整个 Elixir 生态的极简指标核心,通过统一的事件发射与 ETS 快速查找机制,彻底解耦事件采集与上报体系。 - [09:41](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=581s) Phoenix Telemetry 计划通过内置启发式规则,将 BEAM 底层的 Atom 表水位与进程消息队列等信息转化为直观可操作的系统告警。 - [11:09](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=669s) Chris McCord 曾于 2013 年开发 Ruby 版实时库 Sync,后因 Ruby 并发模型的脆弱性而果断转向 Elixir,促成了 Phoenix 的诞生。 - [17:09](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=1029s) 现代 SPA 前端技术栈为了简单的行内校验引入了极高的决策成本、工具链膨胀以及 Node SSR 补丁,形成了严重的过度工程。 - [21:16](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=1276s) LiveView 将类似 React 的有状态组件模型移至服务端,利用 Elixir 进程独立持有会话状态并通过 render 纯函数映射 UI。 - [24:13](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=1453s) 通过 phx-click、phx-change 等声明式绑定,浏览器事件直接触发服务端回调,消除了传统 Controller 中复杂的模板分支逻辑。 - [31:53](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=1913s) 实时表单支持跨大西洋网络传输且体验媲美本地应用,表单输入能够瞬间复用后端的 Ecto 变更集与国际化 Gettext 配置。 - [35:05](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=2105s) 结合 Phoenix PubSub,LiveView 能够以极少量代码实现多页面、多用户的联动变更通知,并在资源被删除时触发平滑重定向。 - [38:59](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=2339s) LiveView 与 Phoenix Presence 结合,无需编写任何客户端 JavaScript 即可实现实时在线用户跟踪与连接数感知。 - [40:41](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=2441s) 通过移植 Marko.js 的 60 FPS 彩虹动画跑分,Demo 证明了即使完全由服务端每隔 16.5ms 计算并推送 diff,浏览器重绘依然丝滑。 - [45:35](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=2735s) LiveView 并非万能银弹,不适用于重度离线、桌面级软件或强依赖乐观 UI 的场景,但在常规交互表单和仪表盘中具备无与伦比的交付速度。 - [48:59](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=2939s) Phoenix 模板渲染达到微秒(µs)级别不仅是展示性能的卖点,更是支撑服务端承载高频实时 UI 吞吐的底层基石。 - [52:21](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=3141s) 在架构设计上,LiveView 通过发送整个表单的序列化数据来避免客户端与服务端状态脱节,多步向导等场景的状态持久化遵循标准 OTP 进程模型。 - [54:54](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=3294s) LiveView 将作为独立的开源库优先以 0.0.1 早期版本快速迭代,未来还将探索借鉴 Absinthe 的批处理机制以优化多用户共享渲染。 ## Detailed walkthrough ### [00:00](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=0s) 开场致谢与 Phoenix 1.4 发布进展 Chris McCord 登台开启演讲,开篇点题本次主题为“回到原点”(going full circle)。虽然台下观众当下还不清楚这个词的具体含义,但他保证稍后揭晓;今天他准备揭晓一些团队一直在研发的令人兴奋的新功能。在阐述设计动机前,他会先带大家回顾一段往事,重温最初让他走进 Elixir 和 Phoenix 的契机。 在进入正题之前,Chris 首先感谢了雇主 DockYard。DockYard 不仅支持他将绝大部分工作时间投入到开源开发中,还为从初创公司到财富 100 强的广泛客户提供服务。接着,他聊到了大家高度关注的 Phoenix 1.4 发布状态:该版本实际上已基本完工,里程碑列表中仅剩最后一个代码格式化器(formatter)相关的 issue。Chris 笑着调侃,这个 issue 本来只需 10 分钟就能改完且已经有了 PR,但他故意没关闭它,因为一旦关掉,社区就会追问:“既然 Phoenix 1.4 都完成了,为什么还不发布?”事实上面临的真正瓶颈是时间与精力——既要写书、改框架,还要准备大会的主题演讲,工作量巨大。团队希望确保文档和配套书籍都准备就绪后再正式发布,避免过早发布导致文档缺失,或是刚发布就不得不立刻赶工 1.4.1 版本的书籍补丁。 Phoenix 1.4 本周内有望推出新的 Release Candidate(RC)候选版本,而 master 分支目前也已相当稳定。新版本将带来 HTTP/2 支持、更快的开发环境编译速度,以及若干提升开发体验的细节改进——例如现在 404 页面中会直接罗列出 Router 中已定义的所有可用路由,Chris 感叹不知为何这么晚才做这个功能,但它对日常开发帮助极大。 - DockYard 支持 Chris McCord 将大部分时间投入到开源维护中。 - Phoenix 1.4 即将发布,代码已基本完成,目前正在完善文档与出版物内容。 - 新特性包含 HTTP/2 支持、开发模式编译加速,以及 404 页面直接展示可用路由等体验改进。 --- ### [02:41](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=161s) 传输层重构、Presence 修复与 Phoenix 新书计划 谈到 Phoenix 1.4 底层的技术改造,Chris 特别感谢了 José Valim 对传输层(transport layer)的重写。Chris 自嘲自己构建系统往往是“自顶向下”的:先构想顶层 API(比如在代码里写个 `channel`,然后期待一条双向通信链路魔法般生效),接着为了实现它再往下补齐需要的部件(比如缺少发布订阅,就顺手写个 PubSub)。这种方式能快速解决手头的问题,但容易忽视架构中正在浮现的通用设计模式。José 介入后,敏锐地识别出了可以抽离的模式,将其重构成更具可扩展性、更小巧的组件。在旧版中,开发者如果要定制 WebSocket 传输逻辑,往往需要整段复制粘贴框架的 WebSocket 传输代码;重写之后,开发者所需编写的定制代码量大大减少。 关于 Phoenix Presence API,Chris 带来了一个“坏消息和一个好消息”:坏消息是,如果开发者正在使用当前基于 JavaScript 的旧客户端,应用内部其实存在竞态条件(race condition);好消息是,团队已通过全新的 JavaScript 客户端彻底修复了该问题,并且新 API 大幅精简了需要编写的前端代码。 对于跳票已久的 Phoenix 官方书籍,Chris 说明原定名为《Programming Phoenix 1.3》,但随着版本迭代,书名已直接变更为面向未来的《Programming Phoenix ≥ 1.4》。全书目前仅剩最后 3 章,计划每两周发布一章,预计完整 Beta 版将在 6 到 8 周内完成,并争取在年底前正式出版纸质版。 Chris 盛赞 José Valim 的工程重构能力时说道: > “He's great at coming in and making things super good that I've made that were just like marginally okay.” - **传输层重写**:WebSocket 扩展变得更加轻量,告别整段代码复制。 - **Presence API 修复**:解决了 JS 端的竞态条件,精简了调用代码。 - **图书进度**:直接适配 Phoenix ≥ 1.4,大会期间提供优惠码 `phoenixworkshop2018`,访问 `phoenixframework.org/book` 可跳转至 Pragmatic Bookshelf 页面。 --- ### [04:49](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=289s) 全新指标体系规范:Telemetry 的诞生与架构设计 Chris 转向了 Elixir 生态中长久以来的核心痛点——指标的采集与上报。从前几次 ElixirConf 开始,团队就希望能系统性解决这个问题,但一直缺乏足够精力。幸运的是,Erlang Solutions 的团队主动联系并希望主导推进,并在 Plataformatec(由 José Valim 参与指导)的协助下,经过约三个月的研发,孵化出了一个轻量级基础库:`telemetry`。 当前生态的痛点在于缺乏统一标准:各种外部服务(New Relic、StatsD、Prometheus 等)各自为政,不仅收集手段五花八门,上报方式也各不相同,实现质量良莠不齐。Telemetry 的定位就是成为整个生态的通用骨干接口,其设计哲学是保持“极简的内核”(tiny, bare-bones core),不引入复杂的元编程,以确保核心 API 极其稳健,避免频繁产生破坏性更新(breaking changes)。 Telemetry 的核心模式非常直观:开发者只需编写一个接收事件的函数,调用 `attach` 监听特定事件。当 Phoenix 处理完 Web 请求或渲染完视图时,内部只需执行 `telemetry:execute/3`。如果有监听器,Telemetry 会高效分发并调用对应函数;若没有任何人监听该事件,该调用在查找 ETS 表后会直接作为空操作(no-op)返回,开销极低。更重要的是,这套机制是与具体框架解耦的——不仅适用于 Phoenix,还适用于 Ecto(如捕获数据库查询延迟与事务生命周期)、Plug、Absinthe(GraphQL 复杂度分析)以及各类 HTTP 客户端(如 Hackney 的外部请求耗时追踪)。在这个稳定的极简核心之上,社区可以自由构建各类上报器(Reporters)和聚合器(Aggregators),将数据输出到 StatsD 或 Prometheus。 Chris 强调保持底层库稳定的重要性时说道: > “The goal is to have this available at large to the greater ecosystem... that they know is not going to be constantly changing or breaking.” ```mermaid flowchart TD accTitle: Telemetry 事件驱动与分发架构 accDescr: 展示应用内部组件触发事件,经由 Telemetry Core 极简分发中心分发至外部 Reporter 的流程。 subgraph Sources["事件发射源 (Event Sources)"] PHX["Phoenix (Web请求/视图渲染)"] ECTO["Ecto (SQL查询/事务)"] HTTP["HTTP Client (外部API调用)"] end subgraph Core["核心分发层 (Telemetry Core)"] EXEC["telemetry:execute/3\n(ETS 快速查找 / 无监听则近乎零开销)"] end subgraph Handlers["消费与上报生态 (Ecosystem & Reporters)"] STATSD["StatsD Reporter"] PROM["Prometheus Reporter"] LOG["Logging / Gauges / Metrics"] end PHX -->|"request:stop"| EXEC ECTO -->|"query:latency"| EXEC HTTP -->|"request:timing"| EXEC EXEC -->|"高效分发"| STATSD EXEC -->|"高效分发"| PROM EXEC -->|"高效分发"| LOG ``` - **统一标准**:终结各监控 SDK 自造轮子的混乱状态,提供通用的采集与上报接口。 - **分层设计**:核心层(Telemetry)仅保留极小 API 面并保证绝对稳定;复杂的聚合与上报交由上层扩展包实现。 - **终极目标**:利用 Erlang Solutions 搭建的坚固地基,未来进一步从运行时中提炼丰富信息,为开发者提供真正具备操作指引价值(actionable insights)的可观测性数据。 ### [09:41](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=581s) Phoenix Telemetry 的愿景:让 VM 内部洞察触手可及 Chris McCord 结合自己在 DockYard 的实际工作经历指出,许多客户即使已经在生产环境中运行 Elixir 多年,也未必是底层专家。他们甚至不知道 Atom Table(原子表)是什么,也不知道自己的应用是否存在内存泄漏;团队往往只观察到应用偶尔崩溃重启,而自动化部署脚本直接将其重新拉起,大家便耸耸肩糊弄过去。事实上,只要有合适的工具,开发者本可以轻而易举地看清这些底层状况。为此,McCord 提出了 Phoenix Telemetry 项目的构想:开发者只需接入 Phoenix,就能打开一个网页,实时观察 Erlang 虚拟机的内部动向。通过内建的简单启发式规则(Heuristics),系统就能主动提炼洞察并发出预警——例如,当 Atom Table 使用率达到 90% 时,系统会提示可能发生内存泄漏并附带参考文档;若内存持续攀升、响应变慢,系统能立即列出消息队列最长的 Top 进程,直接指出性能瓶颈。借助 Telemetry 统一的标准接口,配合 Phoenix Channels,构建一个实时可视化监控面板将变得异常轻松。 ```mermaid flowchart TD accTitle: Phoenix Telemetry 运行时洞察流程 accDescr: 描述从 BEAM 虚拟机底层指标通过 Telemetry 捕获,再经由 Phoenix Channels 推送并在前端实时展示预警的整体链路。 subgraph VM["BEAM 虚拟机运行时"] A["原始系统指标 (Atom 表 / 内存 / 消息队列)"] end subgraph TelemetryPipeline["Telemetry 观测管道"] B["Telemetry 统一事件接口"] C["启发式规则分析 (如: Atom 使用率 > 90%)"] end subgraph PhoenixApp["Phoenix 呈现层"] D["Phoenix Channels 实时通道"] E["实时 Web 仪表盘 (告警与瓶颈提示)"] end A --> B B --> C C --> D D --> E ``` - DockYard 的众多生产客户常年忽视潜在的内存泄漏,服务器挂掉全靠部署脚本自动重启掩盖问题。 - Telemetry 的目标是将原本就存在于 BEAM 中的底层原始数据,转化为直观可读的业务与运维洞察。 - McCord 特别感谢了 Erlang 官方团队以及 José Valim 在启动 Telemetry 规范与工具链建设中所做的基础贡献。 --- ### [11:09](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=669s) 咖啡豆引发的灵感:2013 年的 Ruby Sync 库与 Phoenix 的诞生 在公布下一个重磅功能前,McCord 请在场观众暂时保留评判,并把视线拉回 2013 年。当时他撰写了自己的第一个严肃开源库——基于 Ruby on Rails 的 Sync。他幽默地透露,这个项目诞生的真正催化剂是一整包巧克​​力浓缩咖啡豆(chocolate-covered espresso beans)。他此前从未吃过这种零食,由于本身就对咖啡因上瘾,一口气吃光整包后陷入了极度亢奋、心慌意乱的状态,最终整夜未眠写出了 Sync。可以说,若没有那包浓缩咖啡豆,后来可能根本就不会有 Phoenix。在 Sync 的 README 中,McCord 明确记录了初衷:这是一次由浓缩咖啡豆驱动的思维实验,试图为实时 Web 应用提供一种无需承担沉重 JavaScript 复杂度的可行方案。在 2013 年单页应用(SPA)甚嚣尘上、服务端渲染被嘲讽为“恐龙时代产物”的环境下,Sync 允许开发者保留熟悉的 Rails 开发模式,将渲染好的 HTML 片段推送到浏览器,几乎不需要手写前端代码就能达成实时更新。 > “The goal was I was doing Ruby and Rails at the time I was super productive I loved it and the debate still rages today but in 2013 especially you know everyone was like client-side apps are the future if you're doing server-rendered HTML today you know you're a dinosaur.” —— Chris McCord - Sync 的核心思想:服务端负责渲染 HTML 片段,通过长连接直接将 UI 更新推送到客户端,绕过复杂的前端状态管理与 CRUD 代码。 - 历史时间线:Sync 仓库的首次提交发生于 2013 年 4 月 6 日,大约 6 个月后,McCord 便创建了 Phoenix 项目。 --- ### [13:24](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=804s) 语言并发模型的困局与转向 Elixir Sync 的概念模型虽然成立,演示效果也十分惊艳,但在实际工程中却暴露出致命缺陷。为了在 Ruby 体系中实现 WebSockets 并发,McCord 不得不在 Web 服务旁单独跑一个连接服务;为了避免推送更新时阻塞 Ruby 主线程,他又引入了 EventMachine 这种仿 JavaScript 的事件驱动并发模型。这种嫁接在现实中极不可靠:EventMachine 线程偶尔在静默中崩溃,自身却毫无察觉,McCord 不得不专门编写监控代码去检测线程状态,甚至“主动通知一个 Ruby 库它其实已经死透了”。这些硬伤让他意识到,受制于 Ruby 本身的并发机制,Sync 永远无法达到高可用与高性能。这促使他跳出原有技术圈,探寻其他社区的实时解决方案。正因如此,他在深爱 Ruby 仅半年后便果断转向 Elixir,并在 2013 年底启动了 Phoenix,旨在为这种优雅的实时开发范式寻找真正坚固的并发底座。 > “It worked in that like I was able to make it work, but I was not ever gonna make it robust or performing just because of the concurrency features of the platform.” —— Chris McCord - 在 Ruby 栈中支持实时特性的妥协方案:独立的 WebSocket 进程加上 EventMachine 事件循环,架构脆弱且运维成本极高。 - 难以克服的稳定性隐患:工作线程经常死锁或假死,需要外部代码轮询检测并“通知”其重启。 - 理念迁移:McCord 并没有放弃“无需大量 JS 即可实现丰富体验”的理念,而是将该设计思想完整带到了具备强并发、高容错的 BEAM 平台。 --- ### [15:07](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=907s) Phoenix LiveView 登场:向重前端复杂性宣战 兜兜转转之后,McCord 宣布设计理念迎来了“完全闭环”(Full Circle),正式发布了一项暂定名为 **Phoenix LiveView** 的全新特性。LiveView 继承了当年 Sync 的精神内核,但在底层实现与架构思路上有着天壤之别。它是一个独立于 Phoenix Core 的独立项目,拥有自己的版本控制,直接构建在成熟的 Phoenix Channels 基础设施之上。McCord 直言不讳地指出,虽然有人可能会讽刺这无异于给“老旧马车加装涡轮增压器”(turbocharged horse buggy),但他强调 LiveView 绝非排斥 JavaScript,而是要解决现代前端开发的极高代价。一个典型的业务场景就是用户注册表单:表单转化率直接关乎公司的生死存亡,若每次校验错误都依赖整页刷新,用户流失率将直线上升。因此,开发者被迫引入重型前端框架来优化表单体验,但这一选择背后所附带的架构复杂度已然失控。 ```mermaid flowchart LR accTitle: 现代 Web 复杂决策流对比 LiveView 路线 accDescr: 对比传统重前端选型中的连环复杂决策链路与 Phoenix LiveView 基于 Channels 的轻量服务端渲染路线。 subgraph HeavyFrontEnd["传统单页应用 (SPA) 决策迷宫"] direction TB F1["前端框架 (React / Ember / Angular)"] --> F2["数据通信 (Apollo / GraphQL / REST)"] F2 --> F3["构建打包 (Webpack / Parcel 等)"] end subgraph LiveViewPath["Phoenix LiveView 架构"] direction TB LV1["服务端 Elixir / HEEx 渲染"] --> LV2["Phoenix Channels 双向通道"] LV2 --> LV3["轻量 DOM 差异比对更新"] end ``` - LiveView 的核心主旨:在不引入庞大 JavaScript 复杂度的前提下,为用户提供足以媲美富客户端单页应用的顺滑体验。 - 独立生态定位:LiveView 作为外挂独立包演进,避免使 Phoenix 核心库膨胀,同时充分复用现有的 Channels 传输层能力。 --- ### [17:09](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=1029s) 荒诞的现状:为了一个输入框引发的“架构迷宫” McCord 用一个极其生动的注册表单案例剖析了当代前端开发的荒诞感。用户填写表单时习惯将电话号码留空,点击保存后服务器提示必填;用户随手输入“no thanks”等非法占位符,保存后报错格式不合规;好不容易编造了一个格式合法的假电话,最终提交时又提示邮箱已被占用。在短短三次输入中,普通用户可能被来回拒绝四次。为了消除这种阻力、提供实时行内校验,现代开发团队被卷入了一座令人发狂的“决策迷宫”:仅仅是为了给一个 `<input>` 标签下方实时显示一行错误提示,团队就必须经历无数选型争论与学习成本——是用 React、Angular 还是 Ember?如何与后端通信,是不是得上一套 Apollo 和 GraphQL?前端代码如何打包编译,是用 Webpack 还是别的新工具?McCord 强调,这些繁杂选型虽然是为了解决真实业务问题,但其繁文缛节已经到了极不正常的程度。 > “Just to go from an input tag to some rich user experience around that is a labyrinth of decisions, potential arguments with teammates, learning, just literally take an input tag and then maybe show another label error message beneath it: it's absolute insanity.” —— Chris McCord - 表单提交痛点:缺少实时异步校验的静态页面交互会让用户屡屡受挫,直接挫伤产品转化率与商业价值。 - 业务合理性与技术过载的矛盾:引入富交互完全合理,但为了简单反馈而搭建整套重前端技术栈是严重的过度工程。 - 前端技术膨胀链条:从框架选型(React/Angular/Ember)到传输层协议(GraphQL/Apollo),再到构建构建工具(Webpack),链路层层叠加,开发心智负担急剧膨胀。 ### [18:57](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=1137s) 前端工具链与 SEO 补救带来的复杂度泥潭 Chris McCord 细数了为了给已有 `<input>` 标签增加富交互体验所带来的技术债务:在构建 React 应用时,开发者起初面对 Babel、JSX 和各种 loader 觉得还能接受,但很快教程就会推荐引入 TypeScript,迫使团队为了原本已写好的输入框去学习一门新语言和新类型系统。紧接着,业务部门会合情合理地提出质疑:纯客户端应用导致 SEO 排名骤降,进而拉低了业务转化率。为了挽回 SEO,团队不得不引入 Node 服务端渲染(SSR),在浏览器和现有 Web 服务之间架起一层 Node 缓存与渲染层,执行“服务端输出 HTML 后再到前端数据注水(rehydration)”的繁重架构。 ```mermaid flowchart LR accTitle: Client-Side SSR Complexity Pipeline accDescr: Diagram showing how a client-side architecture evolves into a complex pipeline with Node SSR and hydration. subgraph ClientLayer["客户端渲染"] A["JSX / Babel / Loader"] --> B["TypeScript 学习成本"] B --> C["纯客户端渲染 SPA"] end subgraph ProblemLayer["业务代价"] C --> D["SEO 丢失 / 转化率下跌"] end subgraph SSRWorkaround["SSR 补丁方案"] D --> E["额外部署 Node 渲染服务器"] E --> F["服务端渲染 HTML + 前端注水 (Rehydrate)"] end ``` 这种架构的急剧膨胀,在 Chris 看来很多时候是一场开发者的噩梦。他强调绝非一概否定 JavaScript 单页应用,但在绝大多数业务场景中,团队追求的并非 Google Docs、Google Maps 或 Spotify 网页播放器这类必须保持连续音频流的极重度桌面级体验,而仅仅是介于基础页面与重度富客户端之间的交互。 > “There's so much complexity here to accomplish the rich user experience that we want and it may be justified in some cases but I think in a lot of cases if we had alternatives we would all be better off for it and better off in team size team bandwidth time to market and just developer sanity.” —— Chris McCord - 团队为了简单的输入交互,被迫卷入 Babel、Webpack loader、TypeScript 类型系统等额外工具链。 - 纯客户端 SPA 破坏搜索引擎可见性,逼迫团队搭建 Node 渲染层并处理数据注水(rehydrate),造成维护成本翻倍。 - 只有极少数应用(如音频后台持续播放的 Spotify)必须彻底全盘 SPA 化,绝大多数产品只落在轻重体验的光谱中间。 --- ### [21:16](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=1276s) 从 React Hello World 到服务端的有状态组件 为了解释客户端框架的优势,Chris 展示了 React 官方的 Hello World 时钟组件代码。React 的基础是一个纯函数操作:接受数据或状态,经过 `render` 产生 HTML,这与 Phoenix View 的渲染机制完全一致。然而客户端组件的核心优势在于“有状态(stateful)”:组件内部持有 state,一旦触发定时器修改了 state,React 就会自动调用 `render` 函数刷新 UI。 ```mermaid flowchart TD accTitle: React vs Phoenix LiveView State Loop accDescr: Comparison of the reactive state cycle between React and Phoenix LiveView. subgraph ReactModel["React 客户端机制"] R1["每秒定时器 (setInterval)"] --> R2["setState() 改变状态"] R2 --> R3["重新执行纯函数 render()"] R3 --> R4["Diff 虚拟 DOM 并更新页面 UI"] end subgraph LiveViewModel["Phoenix LiveView 架构"] L1["服务端 Process 接收 tick 消息"] --> L2["handle_info 回调更新 assigns"] L2 --> L3["服务端调用纯函数 render/1"] L3 --> L4["通过 WebSocket 推送至浏览器并 Diff 更新"] end ``` Chris 回忆自己在 2013 年时的思考:Phoenix 拥有极高的渲染速度,而底层 Erlang/Elixir 平台天生就是为维护状态、处理并发事件而构建的。如果服务端本就最擅长持有状态并响应事件,为什么不能把这套“状态变更驱动视图重新渲染”的组件模型直接移到服务端运行?基于这一思路,他直接将 React 的时钟 Demo 移植为一段 Phoenix 代码:利用 Phoenix HTML 的 `~E` 标量直接编写嵌入式模板,视图本身在后台作为一个独立的 Elixir 进程运行。 > “The react model is is my state change render automatically gets recalled and the UI automatically gets updated so if you think about this idea and you think about where I was coming from in 2013 it's like okay we have this super fast language Phoenix is known for its crazy fast rendering and we've got this great platform that actually is great at stateful servers and components like why can't we just put this on the server?” —— Chris McCord - 在 Elixir 服务端,通过 `:timer.send_interval/2` 每秒向自身进程发送 `:tick` 消息。 - 进程的 `handle_info/2` 回调在收到消息后仅需修改 state;一旦 state 改变,框架自动调用纯函数 `render/1`。 - 相比纯客户端框架,唯一的不同只是增加了极低开销的网络传输与少量驱动 diff 的前端 JavaScript。 --- ### [24:13](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=1453s) 实时时钟演示与双向事件绑定交互 Chris 在现场演示了名为 `clock` 的实时路由页面。在 Chrome 开发者工具开启重绘标记时,可以看到每秒钟仅有包含时间的 `<h2>` 标签在闪烁更新。在现阶段的早期实现中,虽然服务端将渲染后的整段 HTML 推送到客户端,但前端会自动执行虚拟 DOM 级别的 diff 对比,确保仅对实际改变的 DOM 节点执行局部重绘。现场观众为此响起掌声,Chris 笑着打趣说没想到这步就鼓掌了,“好戏还在后头”。 紧接着,Chris 剖析了真正赋予客户端应用灵魂的核心——交互性(interactivity)。仅由服务端向下推数据不够,用户必须能同状态产生双向交互。为此,Phoenix 引入了轻量级的事件绑定。他在幻灯片中展示了一个计数器组件(由于代码换行排版违反了 `elixir-formatter` 规则,他还幽默地吐槽为了在幻灯片中向 José Valim 妥协而投机缩减了 `use` 语句)。模板中只需给 HTML 元素加上 `phx-click="decrement"` 或 `phx-click="increment"`,服务端进程就会通过 `handle_event/3` 回调接收这些任意命名的事件并修改数值。 ```mermaid flowchart LR accTitle: Phoenix Event Binding Interaction Loop accDescr: Flow diagram showing how user clicks send events to the GenServer and return updated HTML. subgraph ClientUI["浏览器端"] U1["用户点击按钮 [phx-click]"] U4["前端 Diff 算法执行 DOM 局部替换"] end subgraph ServerProcess["服务端 GenServer 进程"] U2["handle_event/3 接收事件并修改 State"] U3["自动触发 render/1 重新计算 HTML"] end U1 -->|"WebSocket 发送事件"| U2 U2 --> U3 U3 -->|"推送渲染结果"| U4 ``` > “So we have some type of interaction model now and it maps exactly to kind of the flow that we're used to writing on the client but it happens to be sending the events over to the server.” —— Chris McCord - 计数器运行在一个底层的 GenServer 进程中,负责独立持有状态并响应该客户端会话的专属事件。 - 整个双向循环极为精练:浏览器发起交互事件 $\rightarrow$ 服务端调用 `handle_event` 更新状态 $\rightarrow$ 自动触发 `render` 产生新 HTML $\rightarrow$ 浏览器局部应用 DOM 变更。 - 这套模型不仅支持按钮点击,更能无缝扩展到更复杂的业务场景,例如通过表单级别的 `phx-change="validate"` 绑定,在用户敲击键盘时直接将序列化表单传给服务端,以极低成本复用 Ecto 校验逻辑。 ### [29:00](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=1740s) 表单校验与提交:告别传统 Controller 分支 Chris McCord 深入讲解了 LiveView 如何处理表单校验。在传统的服务器端开发中,参数会被送入变更集(Changeset),开发者需要检查其合法性并重新渲染模板;但在 LiveView 中,流程被彻底简化——只需将变更集放进状态中即可。只要状态发生改变,LiveView 就会在服务器端重新渲染并将计算出的差异(Diff)推送到客户端,从而提供即时反馈。Chris 强调,这种方式是“纯零客户端代码”实现前端实时验证的典范。他举了国际化(i18n)的例子:如果要在前端用 JavaScript 做校验,开发者必须考虑如何把所有的 Gettext 翻译文件推送到客户端、设计怎样的数据结构(Payload)、网络传输会耗费多少流量,即便团队经验丰富,配置和维护成本也极为高昂;而 LiveView 却完全没有额外负担,直接在服务端调用现有的 Gettext 即可。 在持久化数据方面,LiveView 提供了 `phx-submit` 绑定。用户在表单上设置 `phx-submit="save"` 后,点击提交不再向普通的 Controller 发起 HTTP POST 请求,而是直接触发当前 LiveView 进程内部的 `handle_event("save",...)` 回调。虽然其 API 外观与传统的 Controller 极为神似,但底层是纯有状态的。这种模型消除了 Controller 中繁琐的模板分支逻辑(例如判断保存失败时究竟要重新渲染 `new.html` 还是 `edit.html`,以及如何传递参数),开发者只需更新状态对象。注册成功后,`redirect` 甚至可以直接指向另一个 LiveView,使传统的 Controller 概念在此处完全退隐。Chris 大胆畅想:“想象一下,未来你能执行 `mix phx.gen.live`,它能完成以前 HTML 生成器做的一切,而且用比现在更少的代码量直接生成一个实时应用程序。” ```mermaid flowchart TD accTitle: LiveView 表单实时校验与提交生命周期 accDescr: 展示用户在浏览器中输入或提交表单时,LiveView 进程在服务端更新状态并推送 diff 的完整闭环流程 subgraph Browser["客户端 (浏览器)"] A["输入框按键 (Keypress)"] B["点击提交表单"] G["客户端 DOM 差异修补 (Diff Patching)"] end subgraph ServerProcess["服务端 LiveView 进程"] C["phx-change 触发: handle_event('validate',...)"] D["更新变更集并写入 State (无需渲染模板分支)"] E["phx-submit 触发: handle_event('save',...)"] F["持久化成功: put_flash 并重定向到新 LiveView"] end A -->|"实时发送验证事件"| C C --> D D -->|"仅推送变更的 HTML Diff"| G B -->|"发送保存事件"| E E --> F ``` - 表单按键时浏览器发出变更事件,LiveView 立即响应并运行变更集校验。 - `phx-submit` 让提交回到已在运行的 Erlang 进程中,避免重复请求和跨控制器跳转。 - 失败时只需更新状态树,省去了手动指定模板和传参的代码分支。 - 成功分支可直接使用 `redirect` 无缝导向另一个 LiveView,省去 Controller 层的中间跳转。 > “在出错的情况下,这其实比大家习惯在控制器里写的模型还要简单……我们不用写分支逻辑去纠结‘我现在必须在这里渲染哪个模板’,我们只需要改变自身的状态即可。” —— Chris McCord --- ### [31:53](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=1913s) 现场演示:跨越大西洋的零延迟实时表单与 CRUD 联动 随后 Chris 进入演示环节,展示了一个看似普通的注册表单页面。在底层,浏览器输入框在按键(Keypress)时会触发实时变更事件并发送给服务端的对应进程。他在现场演示了输入验证:当输入用户名、邮箱或电话号码时,任何不合规的内容会在按键离开手指的瞬间直接爆出错误提示,而一旦输入正确格式,错误信息立刻隐去。Chris 兴奋地回顾了此前和 José Valim 做测试时的场景:他当时将本机的 Localhost 借由网络跨越大西洋穿透映射到身在波兰的 José 那里,从笔记本 Wi-Fi 出发经过大洋跳点,其响应速度居然与本地运行的原生 JavaScript 应用毫无二致,这完全打破了传统 Web 的延迟感知。 紧接着,Chris 开启了进阶的联动演示。他在浏览器左右两边并排打开了两个标签页:右边是用户编辑与展示页,左边则是用户列表页(Index View)。他在右边新注册了一个用户,提交后右侧弹出标准的 Flash 消息,而左侧的列表在未经任何手动刷新的情况下,自动弹出了刚刚新建的用户数据。随后,他将用户名修改为 “José Valim”,点击保存的瞬间,左侧列表的名字同步变更为新名字。最精彩的一幕随之发生:Chris 开玩笑说 José 平时在网上对自己“挑衅吐槽(troll)”太多了,所以决定在此应用中对他进行“永久封禁(Perma-ban)”。当他在一个页面点击删除 José 的账号时,另一个正在浏览该用户详情的页面不仅立刻完成了页面重定向,还直接附带了一条提示“该用户已不存在”的 Flash 消息。 - 跨越大洋的网络测试证明,通过 WebSocket 传输轻量 Diff 的性能足以媲美本地前端框架。 - 单一客户端发起的数据变更,能近乎零延迟地同步到其他打开该页面的客户端。 - 详情页在底层资源被删除的瞬间能够主动感知,并触发带 Flash 提醒的优雅重定向。 > “即便是从我的笔记本 Wi-Fi 出发、跨越大西洋一路跳到 José 那里,它的体验依然和本地的 JavaScript 应用毫无二致。” —— Chris McCord --- ### [35:05](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=2105s) 代码实现解密与实时架构的边界探索 Chris 切回代码编辑器,向观众展示实现刚才全套实时联动所需要的后端代码。展示界面的全部实时逻辑竟然只有不到 20 行代码。在演示架构中,系统通过底层的 `Accounts` 业务上下文与 Phoenix PubSub 协同工作:只要用户被修改或删除,PubSub 就会向对应的 LiveView 进程发送一条消息。在处理删除事件时,代码只需写一个大家最熟悉的 `handle_info` 回调,捕获到用户被删除的消息后,调用 `put_flash` 并执行 `redirect`,该 LiveView 进程便会优雅退出并引导浏览器跳转。而在更新事件中,虽然当前演示只是非常朴素地从数据库重新拉取了一次数据(Refetch),但 Chris 提到,如果应用面对海量并发用户,频繁查库会产生性能瓶颈;他透露自己一直在与 Absinthe(GraphQL)的核心作者 Ben 深入探讨,计划借鉴 DataLoader 以及 Absinthe Subscriptions 解决高并发批量订阅与数据加载的技术方案。 在谈到 LiveView 的能力边界时,Chris 保持了客观理性的技术态度。他明确表示,目前官方团队也还在探索这一模式的极限究竟在哪里。现阶段它极其适合轻量级富交互界面:如无刷新的表单即时验证、后台长任务的实时进度条展示、周期性推送指标的监控仪表盘等,这些用现有的 LiveView 都能极其优雅地解决。但对于像全功能即时通讯(Chat)这样重度依赖客户端复杂状态的大型场景,服务端渲染与 Diff 机制是否合适依然有待验证。Chris 建议开发者先从简单的场景切入,不要过早引入笨重的前端框架。作为无 JavaScript 的最后一个高能案例,他展示了配合 Phoenix Presence 的在线状态监控:在没有写一行客户端 JavaScript 的情况下,仅凭 LiveView 和服务端的 Presence 机制,不同用户登入和在线列表的动态更新便在屏幕两侧实时流动起来。 - 实现完整资源监听、自动刷新和越权/删除重定向的 Show 模块代码量不足 20 行。 - 针对海量客户端同时订阅同一资源导致数据库被击垮的问题,正在探索与 Absinthe DataLoader 类似的批量合并方案。 - 架构设计策略是从表单、进度条、仪表盘等轻量高频反馈切入,在真正需要极其复杂客户端逻辑时再“吞下复杂度去写前端”。 - 演示了利用 LiveView 原生配合 Phoenix Presence,彻底取代了原本在 Phoenix 1.3 中编写 JavaScript 监听在线列表的做法。 > “我们的目标是从小处着手,从那些我们明知能够解决得非常漂亮的简单用例开始,然后再逐步去探索在什么节点上你才应该硬着头皮去承担客户端的复杂度。” —— Chris McCord ### [38:59](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=2339s) 无需编写 JavaScript 的服务端 Presence 实时跟踪 Chris McCord 现场展示了如何在不写一行客户端 JavaScript 的前提下实现完整的在线状态(Presence)跟踪。在 EEx 模板中,仅仅包含一个遍历 `users` 的循环结构(`for user <- @users`),再无任何多余的视图逻辑。当他在浏览器中打开另一个标签页加入应用时,页面立即反映出该用户当前连接了两个设备;而当标签页逐一关闭直至彻底断开连接时,页面上的用户状态瞬时完全消失。这一切全由服务端渲染驱动,开发者只需要像往常一样在初始化时订阅一个主题并声明追踪自身状态即可。 在实现细节上,Chris McCord 坦率承认目前的底层写法是在“作弊”,因为现有方案虽然能跑,但效率尚未优化。Phoenix 的底层有 `Phoenix.Tracker` 和 `Phoenix.Presence` 用于分布式节点间的状态复制,但此前由于不存在类似 LiveView 的功能,团队从未编写过将增量差异缝合在一起的 Elixir 客户端逻辑。在现阶段的临时实现中,每当 Presence 状态变更,服务端会简单粗暴地全量刷新所有状态。Chris McCord 解释道:“In a few lines of server code I have Phoenix Presence features on a server rendered application which is kind of ridiculous.” 尽管在超大规模用户并发下全量刷新不可取,但团队已经在着手开发专门的 Elixir 客户端来处理差异合并。 - 模板层仅使用标准 EEx 循环渲染用户列表,没有编写任何前端组件逻辑。 - 状态同步完全依托服务端 PubSub 主题订阅,在挂载时发起追踪并在断开时自动清理。 - 历史上的 Phoenix 缺乏专用的 Elixir 侧 Presence 客户端,当前代码仅是粗暴的全量重拉,后续将提供细粒度的增量状态合并机制。 --- ### [40:41](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=2441s) 性能质疑与极致的 60 FPS 彩虹动画 Demo 面对“服务端渲染能否承受规模化并发与高频计算”的典型质疑,Chris McCord 坦陈,虽然 BEAM 虚拟机极其出色,但这确实不可避免地会消耗更多服务器 CPU 周期。为了用极端案例证明性能瓶颈并非出在渲染层,他展示了一个看似荒诞却切实运行的硬核演示。José Valim 在调研客户端 DOM diff 库时,注意到了开发 morphdom 的团队还构建了 Marko.js。Marko.js 官方有一个著名的性能跑分示例:页面在 60 帧每秒(FPS)的高帧率下,反复计算并重新渲染 85 到 100 个 `<div>` 标签的色彩与旋转角度,全程完全不依赖任何 CSS 关键帧动画,纯靠极速模板重新渲染并动态设置样式属性。 José Valim 提出一个疯狂的想法:“为什么不用 Elixir 试试?” 于是 Chris McCord 将 Marko.js 的纯前端逻辑逐行移植到 Elixir 中。为了维持 60 FPS,服务器必须使用定时器每隔 16.5 毫秒向自身投递一条消息,触发状态变更、重新渲染并将更新推送到浏览器。Chris 调侃道,当这个功能第一次跑通时他和 José 之间的短信充满了“不可描述的感叹词”。现场演示中,完全由服务端实时驱动、毫无优化的 60 FPS 彩虹动画在浏览器中丝滑流动,引发了台下热烈的掌声。随后他们还展示了交互能力:为元素增加一个 `phx-click` 绑定,仅需在服务端接收事件并将步进参数乘以 -1,整个 60 FPS 动画的旋转方向便即刻逆转。 ```mermaid flowchart LR accTitle: LiveView 60 FPS 渲染循环 accDescr: 展现服务器内部高频定时器驱动 LiveView 重新渲染并把补丁推向前端的闭环过程。 subgraph SVR["Phoenix 节点 (BEAM 进程)"] T["定时器 (每 16.5ms)"] -->|"投递消息"| S["更新状态 (Hue / Rotation)"] S -->|"调用 render/1"| R["生成 HTML 树"] R -->|"序列化 JSON 补丁"| P["推送 Diff"] end subgraph CLI["浏览器客户端"] P -->|"WebSocket"| W["morphdom 差异补丁"] W -->|"60 FPS 视图重绘"| D["DOM 更新"] D -.->|"phx-click (反转方向)"| S end ``` - 该动画渲染完全绕过了 CSS 动画系统,所有旋转与颜色变换都在服务端由三角函数(`Math.sin`)实时算得。 - 当前 Demo 甚至未经性能调优:底层先将 IO Data 转成 Binary 再打包为 JSON,未来若直接以 IO Data 流式下发,吞吐能力还将大幅提升。 - 动画交互只需三行代码:捕获客户端点击事件,在 GenServer 回调中将运动方向反转,即可瞬间改变高频渲染链路。 --- ### [45:35](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=2735s) 权衡取舍:LiveView 的适用边界与真实优势 虽然演示效果极其震撼,但 Chris McCord 强调必须理性看待 LiveView,不能将其视为包治百病的银弹。针对复杂桌面级 Web 软件、强依赖离线工作模式(Offline-first)的应用,或者必须具备乐观 UI 更新(Optimistic UI)以消除感官延迟的场景,传统富客户端 JavaScript 框架仍然是不可替代的最佳选择。如果客户端与服务器之间的网络延迟极高,且每个交互都需要经历一次完整的网络往返,LiveView 的体验也会大打折扣。 但在实际生产中,绝大多数日常业务并不需要这种极端的客户端复杂度,LiveView 能够为团队带来无与伦比的交付速度与开发生产力。Chris 举例指出,在 LiveView 中实现表单的行内实时验证仅需 30 秒,而即使是经验丰富的 JavaScript 开发者在前端做状态管理与通信可能也需要数小时,初学者甚至要花上数天。Chris McCord 直言:“If you want like great productivity or a great time to market, I think we're going to be unbeatable in that regard.” 此外,从长远综合成本考量,首屏加载时让用户下载 10 MB 的庞大 JavaScript 打包体积往往比几毫秒的网络往返更加难以忍受;而 LiveView 的客户端基础运行时仅有大约 60 行代码,同时开箱即享服务端原生渲染带来的极佳 SEO 支持。 - **不适用的场景**:离线应用、无网络容错、对网络往返完全零容忍的复杂乐观 UI、重度依赖原生浏览器底层 API 的桌面级软件。 - **核心业务胜势**:表单校验、实时面板、状态追踪等大多数服务端交互场景,开发时间可从数天压缩至数十秒。 - **微量体积与 SEO**:初始客户端逻辑仅约 60 行,避免了数兆字节的前端构建物包袱,且所有路径请求均直接输出首屏标准 HTML。 --- ### [48:18](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=2898s) 微秒级渲染与 Phoenix 的生态标志 Chris McCord 探讨了 LiveView 之所以能在技术上成立的根本支柱:Phoenix 极度恐怖的渲染速度。Phoenix 的模板与视图通常能够在“微秒”(microseconds, `µs`)级别完成处理,这一直是 Phoenix 社区非常醒目的标志性特性。Chris 笑着回忆,这其实多亏了 Phoenix 核心团队成员 Jason(Jason Stiebs)。 当初如果让 Chris McCord 自己来实现日志格式化输出,他大概只会机械地打印诸如 `0.4 ms` 之类的浮点数。是 Jason 别出心裁地引入了微秒字符(`µs`),让日志在控制台上直接打出微秒时间。Chris McCord 感慨道:“I think this is like the gift that keeps on giving because people see this like 'What's this micro character?' and they're like 'Oh my god.'” 这一细节不仅在开发者群体中引发了巨大的震撼和口碑传播,也构成了今天团队有底气将高频高动态 UI 推送放回服务端运行的性能基石。 - 控制台中直接输出希腊字母 `µs` 取代了常规的 `0.x ms`,在心理上给开发者带来了强烈的速度震撼。 - 微秒级的极速渲染能力不仅是出色的品牌宣传点,更是确保 LiveView 能够高频吞吐 DOM 差异的硬性技术前提。 ### [48:59](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=2939s) 性能的真正意义:不仅是吹嘘的资本,而是推动技术前沿 Chris McCord 谈到了社区对于微秒级性能的讨论。外界常有质疑声认为,用户在 40 毫秒还是 400 微秒内拿到页面根本毫无区别,这种极致的优化似乎只是为了“吹嘘的资本(bragging rights)”。Chris 承认这种说法有一定道理,它确实是一个绝佳的营销卖点,但 Elixir 和 Phoenix 的追求绝不仅止于此。他提到自己读过一篇对比 Ruby 与 Elixir 等 Web 框架性能的博文,文中提出了两个核心论点:第一,是否存在“足够快的 Web 应用”?Chris 表示赞同,如果局限于过去十年间构建的那种传统应用,40 毫秒与 400 微秒对终端用户而言确实毫无感知差异。 但那篇博文引发深思的第二个论点在于:在选择 Web 框架乃至编程语言时,性能到底有多重要?Chris 旗帜鲜明地指出,性能极其关键,因为它是突破技术瓶颈的核心驱动力。如果开发者只是想重复制造过去已经存在的系统,性能确实没那么重要;但若想超越现状、在功能交付速度上击败竞争对手,底层平台的性能就起着决定性作用。卓越的性能赋予了开发者重塑 Web 开发范式的底气,并将生产力提升到全新的维度。正如 Chris 所总结的: > “The microsecond is going to stay there, but just realize it enables us to produce incredible things, not just to brag about them, even though we do brag.” - 传统应用对 40 毫秒与 400 微秒的响应差异并不敏感,常规场景下确实存在“足够快”的应用。 - 框架与语言的选择决定了能否“推进技术前沿(advance the state of the art)”。 - 极致性能的价值不是数字游戏,而是为创新应用架构(如 LiveView 这种全服务端驱动模式)提供算力与延迟裕量。 --- ### [50:56](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=3056s) 未来在你们手中,更由你们亲手构建 Chris 引用了此前 José Valim 在大会演讲中的论点。José 曾提到“未来就在你们的手中(the future is in your hands)”,Elixir 核心团队已经把工具和基础设施搭建完毕,社区成员完全有能力为语言添加功能并塑造其走向。Chris 在此基础上做出了进一步延伸,激情呼吁大家去亲手创造未来。他指出,Elixir 和 BEAM 平台能够做到其他平台根本无法实现的事,开发者应当拥抱这种能力,勇敢地挑战技术现状。 他特别举了 Nerves 物联网项目的例子:如今开发者可以在户外边缘设备上直接部署运行 Phoenix,并通过 Phoenix Channels 与后端通信。Justin(在早先演讲中)演示的案例彻底震撼了他——现场的硬件设备仅仅依靠从后端服务器接收到的一条频道消息,就能自动完成固件更新。Chris 坚信,只要社区不断拓展边界、切入更多领域,Elixir 生态将会取得不可估量的成功。他在全场热烈的掌声中结束了演讲,并开启了现场问答环节。 > “Not only is the future in your hands, the future is yours to build; like we have this platform that can literally advance the state of the art, do things that other platforms simply can't do.” --- ### [52:21](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=3141s) 现场问答:LiveView 的客户端状态、多步向导与持久化机制 现场观众提出的第一个问题围绕用户状态展开:当客户端与服务端都存在状态时,框架如何管理这些状态?如果遇到多步骤表单(Wizard 向导)这类场景,进程崩溃或用户中途离开该如何保证数据持久性(Durability)? Chris 解释道,多步骤向导其实是 LiveView 的绝佳应用场景——用户每完成一步,服务端就推送更新后的 HTML。为了彻底消除客户端与服务端状态不一致的问题,LiveView 的设计理念是将所有状态收拢在服务端。在表单处理上,`phx-change` 和 `phx-submit` 绑定机制会显式地序列化并发送“整个表单的数据”,而不仅仅是发生变动的单项输入。这样即使网络存在延迟,服务端也不会因为收到局部片段而误判用户的输入状态。 关于数据持久化,Chris 指出 LiveView 的连接在服务端本质上就是一个普通的 Erlang/Elixir 进程(Process),因此处理持久性的方式与处理任何有状态系统完全一致。开发者可以在向导的每一步操作(如点击“下一步”)时将整个表单状态写入数据库,也可以像传统 Controller 那样按需持久化,或者设置定时任务每隔几秒对状态进行快照转储。 ```mermaid flowchart LR accTitle: LiveView Form State Synchronization accDescr: Browser sends full serialized form state to the server LiveView process, which updates state and optionally persists it to storage. subgraph Client["客户端(Browser)"] Form["整个表单数据\n(phx-change / phx-submit)"] end subgraph Server["服务端(BEAM / LiveView)"] Proc["LiveView 进程\n(持有所有会话状态)"] Storage[("数据持久化\n(Ecto / DB 快照)")] end Form -->|"完整序列化传输"| Proc Proc -.->|"按步骤或定期持久化"| Storage Proc -->|"推送局部 HTML 差异"| Form ``` - LiveView 避免了在前端做复杂的防抖补偿,依靠传输整表单数据确保服务端持有权威状态。 - LiveView 进程是轻量级的有状态 BEAM 进程,崩溃恢复和状态持久化遵循标准的 OTP 架构模式。 --- ### [54:54](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=3294s) 现场问答:合并渲染、发布计划与分布式负载均衡 后续几位观众针对架构细节与发布时间提出了具体问题: 针对“当数百名用户同时查看相同的 HTML 片段时,服务端是否会进行合并渲染(Coalescing)”的疑问,Chris 透露目前每个连接独立渲染,但未来必然会进行深度优化。事实上,Phoenix Channels 早已通过名为“快速通道(Fast Laning)”的机制解决了类似问题:在一个拥有上千名在线用户的聊天室中向 Channel 广播消息时,系统默认只将 JSON 载荷序列化渲染一次,然后并发分发给所有客户端,除非开发者显式声明要拦截该事件为特定客户端定制内容。团队计划将相似的优化思想移植到 LiveView 的 HTML 片段合并中。 关于“何时可以使用 LiveView”,Chris 表示会很快推出。LiveView 是独立于 Phoenix 核心代码库的独立库,这使它无需等待大版本周期,能直接以 `0.0.1` 这种早期版本形式发布,并快速迭代至 `1.0.0`。但他打趣地提醒大家,目前的头号要务是发布 Phoenix 1.4,请大家先别转移他的注意力;此外,早期版本的公共接口和 API 极有可能发生大幅重构。 针对负载均衡器后挂载多台 Web 服务器时如何定位用户会话的提问,Chris 回应称这在 BEAM 生态下“自然可用(it would just work)”。由于连接基于 WebSocket 建立,客户端在负载均衡后会与特定节点保持长连接;即使发生断线重连,Phoenix PubSub 原生具备分布式能力,且客户端持有代表服务端进程标识符(PID)的序列化 Token。在分布式 Erlang(Distributed Erlang)集群环境下,节点间可直接跨机通信寻址,开发者基本无需为多机路由费心。 最后,有观众反馈演示文稿中的购书优惠码失效,Chris 澄清该促销代码仅适用于 Pragmatic Bookshelf 官网,在亚马逊(Amazon)无法使用。在感谢现场听众后,演讲在掌声中正式落下帷幕。 - **快速通道(Fast Laning)机制**:Phoenix Channels 广播时默认只渲染一次 Payload,LiveView 未来计划借鉴此机制优化通用 HTML 片段渲染。 - **发布节奏**:优先保证 Phoenix 1.4 正式发布,LiveView 将作为独立包以 `0.0.1` 起步快速演进。 - **分布式寻址**:基于 WebSocket 长连接、Phoenix 分布式 PubSub 以及客户端序列化的 PID Token,实现多节点集群透明调度。 ## Notable quotes > [50:56](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=3056s) Not only is the future in your hands, the future is yours to build; like we have this platform that can literally advance the state of the art, do things that other platforms simply can't do. — Chris McCord > [17:09](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=1029s) Just to go from an input tag to some rich user experience around that is a labyrinth of decisions, potential arguments with teammates, learning, just literally take an input tag and then maybe show another label error message beneath it: it's absolute insanity. — Chris McCord > [35:05](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=2105s) 我们的目标是从小处着手,从那些我们明知能够解决得非常漂亮的简单用例开始,然后再逐步去探索在什么节点上你才应该硬着头皮去承担客户端的复杂度。 — Chris McCord > [45:35](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=2735s) If you want like great productivity or a great time to market, I think we're going to be unbeatable in that regard. — Chris McCord > [48:59](https://www.youtube.com/watch?v=Z2DU0qLfPIY&t=2939s) The microsecond is going to stay there, but just realize it enables us to produce incredible things, not just to brag about them, even though we do brag. — Chris McCord ## Mentioned Chris McCord, José Valim, Jason Stiebs, DockYard, Erlang Solutions, Plataformatec, Phoenix Framework, Phoenix LiveView, Phoenix Channels, Phoenix Presence, Telemetry, Elixir, Erlang, BEAM, Ruby on Rails, Sync, EventMachine, Ecto, Plug, Absinthe, React, morphdom, Marko.js, Nerves, Pragmatic Bookshelf [Source](https://www.youtube.com/watch?v=Z2DU0qLfPIY)

Source quotes

[50:56] Chris McCord: Not only is the future in your hands, the future is yours to build; like we have this platform that can literally advance the state of the art, do things that other platforms simply can't do. [17:09] Chris McCord: Just to go from an input tag to some rich user experience around that is a labyrinth of decisions, potential arguments with teammates, learning, just literally take an input tag and then maybe show another label error message beneath it: it's absolute insanity. [35:05] Chris McCord: 我们的目标是从小处着手,从那些我们明知能够解决得非常漂亮的简单用例开始,然后再逐步去探索在什么节点上你才应该硬着头皮去承担客户端的复杂度。 [45:35] Chris McCord: If you want like great productivity or a great time to market, I think we're going to be unbeatable in that regard. [48:59] Chris McCord: The microsecond is going to stay there, but just realize it enables us to produce incredible things, not just to brag about them, even though we do brag.

Original transcript

[0:00] [Music] [0:28] so we're talking about going full circle [0:30] today which you don't know what that [0:32] means yet but later you'll find out so I [0:34] have some exciting features that I want [0:36] to unveil that we've been working on but [0:39] in order to understand the motivations [0:41] we have to kind of take a trip back in [0:43] time to what got me into a Luxor in [0:45] Phoenix in the first place but before [0:48] that I was at the plug Dockyard you've [0:50] heard a lot about Dockyard I'm sure [0:52] you're tired of it but they do support [0:53] me working on open source the majority [0:56] of my time so we work for we work for a [0:59] lot of different clients from startups [1:00] to Fortune 100 companies so you need [1:03] elixir support definitely check us out [1:05] and give them a big big THANK YOU yeah I [1:08] did get raffled out so looking forward [1:10] to those two hours so Phoenix one four [1:13] we've heard about for the last couple [1:15] talks that I gave it's out real soon [1:18] it's actually almost about finished and [1:22] so people watch the milestone list and [1:23] there's one outstanding issue which is a [1:25] formatter issue that looks like it [1:26] should be like take 10 minutes to [1:28] complete and actually the only reason I [1:30] haven't closed that there's a PR for it [1:32] and if I close it then people are gonna [1:34] be like if Phoenix one floor is done why [1:35] is it not released yet so it's it's [1:38] baked at this point but writing it turns [1:41] out writing a book [1:42] and working on a framework before a [1:44] getting ready for keynote it takes a lot [1:46] of time so we're just waiting for the [1:49] bandwidth to make sure the docs are [1:51] ready for a release and make sure the [1:54] phoenix book is in a good state before [1:55] we stamp it because we don't want to [1:59] ship things too early and not have the [2:01] docs ready or have to immediately work [2:03] on a new one for one edition of the [2:07] Phoenix book that has been over a year [2:08] past due but it is coming so Phoenix 1 4 [2:13] it should be a new release candidate we [2:15] were to try to get it today so I could [2:16] drop it Apple style like but sorry it's [2:19] a you know if you use a master it's [2:20] quite stable we should have a loose [2:21] candidate I think this week but keep an [2:24] eye on that we're gonna have HTTP to [2:26] support coming we have faster [2:27] development compilation as well as some [2:29] other kind of quality of life [2:30] improvements the 404 page now shows the [2:33] routes in your router that exists I [2:35] don't know why it took us this long to [2:37] add it but [2:38] - super huge help even for me day-to-day [2:41] we have overhauled transport layer and [2:43] this is something that Joe's a did so a [2:45] huge thank you to Joe's a for this he's [2:47] great at coming in and making things [2:50] super good that I've made that we're [2:52] just like marginally okay so the Phoenix [2:55] channel layer initially like I always [2:57] say like I I stack up problems like [2:59] top-down so it's like oh i if i could [3:01] just type channel here and then like the [3:05] there'd be this bi-directional line of [3:07] communication and like things would just [3:08] work and i work my way down and it's [3:10] like oh did you that I need to pub/sub [3:11] layer so I got off the right pub/sub so [3:13] anyway I worked top-down and I make the [3:15] thing work that I wanted to accomplish [3:17] but I don't quite think about the [3:20] patterns that are emerging in the [3:22] architecture so then Josie came in and [3:25] he's like oh we actually have some [3:27] patterns we can lift out of here to make [3:28] it much more extensible and build it [3:30] into smaller pieces so with Phoenix one [3:31] for Joe's A's pretty much completely [3:33] rewritten the transport layer so now if [3:35] you want to if you want to extend the [3:37] WebSocket transport you used to have to [3:39] like copy paste our entire WebSocket [3:40] transport now there's much less code for [3:43] you to write so big thank you Joe's a [3:44] for that some bad news with the presents [3:46] API if you're using the president's [3:48] JavaScript you have a race condition in [3:50] your app the good news is we fixed it [3:52] with new JavaScript but the API did [3:55] improve so at least there's some bad [3:57] news but some good news in that it's [3:58] actually less JavaScript - right and I [4:02] mentioned the phoenix book this is a [4:03] long past due it was supposed to be [4:05] programming in phoenix 1 3 and it is now [4:07] a funder grooming in phoenix greater [4:08] than 1/4 so we just skipped the 1 3 and [4:11] went straight for a book that should be [4:13] relatively future proofed and there is a [4:16] discount code available for this [4:17] conference phoenix workshop 2018 [4:20] hopefully that's not case-sensitive [4:22] they could have made an easier code I [4:24] feel like but if you go to Phoenix [4:26] framework slash book it will redirect to [4:27] the Prag bog page so commit that to [4:31] memory yeah it's I think it's available [4:32] for the next 5 days or so but definitely [4:34] check that out [4:35] it's there's three chapters left so [4:37] should be out new chapters every two [4:40] weeks so if things go to plan the full [4:43] beta book should be done in six to eight [4:44] weeks and ideally out in print by the [4:47] end of the year [4:49] so now I want to talk about another [4:51] project that has I've mentioned metrics [4:54] going back almost I think last lexicon [4:57] before about wanting to tackle the [5:00] general problem of collecting metrics [5:01] and reporting metrics in the elixir [5:03] ecosystem and we haven't had the [5:06] bandwidth to get this going but [5:07] fortunately the Erlang solution folks [5:09] reached out to the Phoenix team and they [5:11] said hey we are really interested in [5:13] getting involved in this and with the [5:15] help of Joe's a a plataforma tech kind [5:17] of mentoring and overseeing this about [5:19] three months so they started work on an [5:22] initial effort in this area that we're [5:25] calling telemetry and telemetry as goal [5:27] is to be this initial bare-bones metrics [5:30] library that everyone can use that way [5:32] we have a common interface to start [5:34] collecting and reporting metrics because [5:36] the problem in the community now is we [5:37] have all this information we have [5:39] various ways that collect information we [5:40] have a ton of different ways to report [5:42] information but whether you're using [5:44] like new relic stats D all these [5:46] different services have all these [5:47] different ways to collect and it it's [5:49] either at best there's reasonable [5:51] interfaces but most of the time everyone [5:53] implements things in their own way [5:54] sometimes they work great sometimes they [5:56] don't so we're trying to have a common [5:57] interface that everyone can use and [5:59] extend and the goal right now is just a [6:02] very tiny API so because if we ship this [6:06] and we have a lot of people depend on it [6:07] we don't want to be constantly breaking [6:08] like you know core libraries in the [6:10] ecosystem so right now we've thought and [6:12] put together a very small interface to [6:15] say very similar to kind of like Phoenix [6:17] instrumentation used to work where you [6:19] just wanted to have a call back run [6:20] based on some event happening in the [6:23] Phoenix application for example so with [6:26] a telemetry today you just write a [6:27] module and a function and there's no [6:30] meta programming here nothing fancy like [6:31] we were doing in Phoenix you just want [6:33] to say okay you know what if there's a [6:34] web request a minute let's say the web [6:36] request was sent I want to be able to [6:37] log latency so I define a function and [6:40] then I can tell telemetry hey I want to [6:43] attach to this event I don't care where [6:45] it's coming from I know that the web [6:46] firm I'm using is gonna admit this and [6:48] telemetry is gonna use it and it's [6:50] dispatch enterin way to very efficiently [6:52] and quickly call this function for you [6:54] and that way when Phoenix runs not today [6:58] but when we integrate with telemetry and [6:59] things solidify Phoenix internally is [7:02] good at run telemetry [7:03] execute and for this example if we [7:05] finish a web request we may omit request [7:07] stop if we send a requests or render a [7:11] view we're gonna we can omit these [7:13] actions and if no one's attached to them [7:16] it's going to be almost a free dispatch [7:18] so it's just gonna look up a nets and do [7:20] nothing and be a no op otherwise people [7:22] can hook in to everything that's [7:23] happening in the framework and this is [7:25] great and this works just like it would [7:27] work with Phoenix instrumentation but [7:29] the goal is you know this should be not [7:31] Phoenix related at all right because you [7:33] should be able to say if I want to hook [7:36] into ecto lifecycle insecto queries if [7:40] there's a DB query that your transaction [7:42] that was just committed I maybe I want [7:44] to log the latency of that transaction I [7:46] should be able to attach to that event [7:49] in telemetry and then ecto internal link [7:51] and say telemetry executes and passes [7:53] metadata to you and then you can do what [7:55] you want to do with it and that's it's a [7:58] very very simple core API because like I [8:01] said the goal is to have this stable [8:03] rock-solid tiny core because if we have [8:06] this available to the ecosystem when [8:08] we're constantly releasing varakin [8:09] changes and then you know what's the [8:10] point so you know the goal is to have [8:12] this available at large to the greater [8:14] ecosystem whether it's ecto phoenix plug [8:17] the Absinthe wants to log like you know [8:19] complexity analysis anyone that's [8:21] interested in what's happening in the [8:22] app and depend on this tiny tiny core [8:24] that they know is not going to be [8:25] constantly changing or breaking and even [8:28] for things like HTTP clients it's [8:29] important because you often want to know [8:31] if you make a external request to some [8:33] web servers you want to hook into events [8:35] like how long did that take [8:36] and I think internally Hackney does have [8:39] various HTTP clients do have ways to [8:41] hook into these events but again like I [8:43] said there's no common interface so [8:46] symmetry is going to solve that with a [8:48] tiny core and then on top of telemetry [8:51] we can have reporters and aggregators [8:53] and other packages that add add more [8:55] features like they can report to stats D [8:57] they can report to limit report to [9:00] Prometheus and and then again we can [9:04] also provide things like I mentioned [9:06] last year at Alexa comp like gauges [9:08] being able to probe different values on [9:11] the system report requests per second [9:12] but that would be in another [9:14] age not on top of the telemetry core and [9:18] my personal goal is to come in and then [9:21] make use of this hard work by the Erlang [9:23] solution folks and obviously team up [9:25] with them but for me they've solved the [9:27] hard problem so far which allows me to [9:29] accomplish my personal goal with this [9:31] whole metrics initiative a1 is we need [9:33] to have this common interface but for me [9:35] personally it's how do we lift this rich [9:37] information out of the system and [9:38] provide actual actionable insights for [9:41] people that aren't experts because at [9:43] Dockyard we work with a lot of clients [9:44] even if they've had a looks or in [9:46] production for years they may not even [9:48] be aware of like what the atom table is [9:49] or that they're leaking memory all they [9:52] know is that maybe the a pair is out [9:54] occasionally and their deployment [9:56] process just rebooted the server and it [9:57] just becomes one of these like hmm I [9:59] don't know like they shrug when they [10:01] could just look at look this information [10:03] if they just had the tools to do it so a [10:05] Phoenix telemetry project is what I want [10:07] to accomplish where if you listen to [10:10] Phoenix and you can have a web page load [10:13] up in in real time you're seeing what's [10:14] happening in the VM and we should be [10:17] able to easily lift information and [10:18] insights to people right if you have a [10:20] if you're not 90% usage of your atom [10:23] table you probably have a memory leak so [10:25] we could write an if condition right we [10:26] hit simple heuristics do should be able [10:28] to say like whoa you might be leaking [10:30] atoms here here's what's going on what [10:32] may be happening by the way here's some [10:33] information to go read about so I want [10:36] to be able to quickly answer you know if [10:38] you see memory continuing to climb if [10:40] the app slow we should be able to say [10:42] here's the top processes by message [10:44] queue this is probably a bottleneck [10:46] these are things that we should be able [10:47] to get at and with telemetry in this [10:49] common interface it's gonna make it [10:50] super simple to build a you know web app [10:53] with channels just to hook into this [10:54] data and just give people insights from [10:56] that raw data that was always there so [11:00] I'm super excited that this is actually [11:02] underway and again a big thank you to [11:03] their link folks and josée to kind of [11:06] kick off this effort and I'm really [11:09] super excited about the next future that [11:11] we've been working on the caveat is it's [11:13] still very early and we have to take a [11:16] trip back in time really to understand [11:17] my motivations and I would say withhold [11:20] judgment here until we get to be in [11:21] because I think some people initially [11:23] will find this interesting let's say [11:27] so if we go back in time to 2013 I wrote [11:31] this library called a sink anyone [11:33] remembers sink real time real especial [11:35] named one from Ruby alright three people [11:39] over there yes so this was actually my [11:41] first real open source initiative I'd [11:43] written a couple libraries so it's kind [11:45] of near and dear to my heart that I put [11:46] myself out there but also I was trying [11:49] to experiment with an idea here and to [11:53] be honest this as so this is this is [11:55] what led to Phoenix but this is also [11:57] because one night I ate like a whole bag [12:00] of chocolate-covered espresso beans and [12:02] threw this true story so like I don't [12:05] know where we got these two espresso [12:06] beans I never had them before and I'm [12:08] addicted to caffeine but either way [12:10] there's a lot of caffeine in these [12:12] things so like I started eating them [12:13] like oh these are delicious and I ate [12:14] like the whole bag and like I didn't [12:16] know that like that was good like I was [12:18] freaking out so anyway I stayed up all [12:20] night and sink as what fell out of that [12:22] I'm serious that's what happened that's [12:25] ultimately Phoenix prolly wouldn't be [12:27] here if I hadn't eaten that bag of [12:28] espresso beans but if you go to the [12:32] readme of this project though you'll [12:34] find like my motivations were it was a [12:36] thought experiment [12:37] fueled by espresso beans that I wanted [12:40] to grow into a viable option for [12:41] real-time applications that didn't have [12:44] to require a ton of JavaScript [12:45] complexity so the goal was I was doing [12:48] Ruby and rails at the time I was super [12:51] productive I loved it and the debate [12:53] still rages today but in 2013 especially [12:56] you know everyone was like client-side [12:58] apps for the future if you're doing [13:00] server rendered HTML today you know [13:02] you're a dinosaur so at that time I was [13:04] like you know what how can I get some of [13:06] this real-time rich experience but not [13:08] ditch the stack that I love and and you [13:12] know have to no rails but we basically [13:13] were able to render bits of HTML and [13:16] push update those to the browser with [13:18] seemingly no JavaScript and croud [13:20] involved and BAM real-time application [13:24] so this worked but unfortunately did not [13:27] work well so this was 2013 and the [13:32] conceptual model worked and it actually [13:34] feature-wise [13:35] was a compelling demo but the problem [13:38] was to make it work [13:39] it kind of led me away from Ruby because [13:42] I had to to make this work so next to my [13:45] web server in Ruby and rails I had to [13:48] start a nother server for WebSocket [13:50] connections and to not block the main [13:53] Ruby thread to publish updates I had to [13:56] run an event loop in Ruby so I didn't [13:58] run like a Java JavaScript concurrency [13:59] model brought that into Ruby to publish [14:02] events and that worked but then [14:04] sometimes the event machine thread would [14:06] die but it wouldn't know so I had to [14:07] write code to like check a thread and [14:09] tell a ruby library that hey you [14:11] actually are dead and you're not aware [14:13] of it so it was like to me it was clear [14:16] that this was and this worked in that [14:18] like I was able to make it work [14:20] but I was not ever gonna make it robust [14:22] or performing just because of the [14:24] concurrency features at the platform but [14:27] I love the way I could write [14:28] applications on the platform so that's [14:30] when I started looking around into [14:32] various languages how are other [14:34] communities tackling real-time [14:36] applications what can I do to kind of [14:38] have the best of both worlds and if you [14:41] look at my commit history I started [14:43] first commit on sync was April 6 2013 [14:45] and I started Phoenix about 6 months [14:48] later so I started sync you know in love [14:51] with Ruby thinking this is what I was [14:53] going to do for you know the foreseeable [14:55] future of my professional career and [14:57] then six months later I was already into [14:59] a look so work learning and kind of [15:00] thinking how can I accomplish this but [15:03] in a ecosystem in language were I can [15:05] execute on this goal and have it work [15:07] well so that's my background into [15:11] accomplishing real time rich [15:13] applications without a bunch of complex [15:16] JavaScript and that's where we've come [15:18] full circle with a feature that we're [15:20] tentatively calling Phoenix live view [15:22] and it's essentially what we just talked [15:25] about with sync if you view sync don't [15:26] freak out because it's in spirit the [15:29] sync model but very different [15:30] implementation and approach but the goal [15:32] is we can provide rich user experiences [15:34] on par with client-side applications [15:37] without the mountain of complexity [15:38] because I think even if we're [15:40] professional JavaScript developers there [15:42] is a cost that we have to bear and it's [15:44] certainly very high today to get [15:45] moderate user experiences and I know [15:48] people here are going to say like you're [15:50] building a turbocharged horse buggy like [15:52] this is not the way to go [15:53] we've been down this path but give me a [15:56] second [15:57] this is also a separate project outside [15:59] of Phoenix core so it's gonna live [16:01] separately it's gonna be version [16:02] differently but it's gonna build on top [16:04] of the tooling that we already have like [16:06] channels but we have to ask ourselves [16:10] like why do we write JavaScript today [16:12] like I'm not saying Java scripts bad and [16:14] you should never write it the goal is [16:16] rich user experiences so like we I think [16:18] a lot of us are aware that there is a [16:19] huge cost to bear but it's a fact of [16:21] life because we want to provide a good [16:25] UI so like a registration form is the [16:27] great example like it makes business [16:29] sense it makes like dollars like our [16:31] business is going dollars since our [16:33] business is going to either be [16:34] successful or fail based on people [16:35] actually converting to you registered [16:37] users so if we have a registration form [16:40] and a user has to like click and see a [16:42] page refresh just to get an error that's [16:44] gonna lead to less users signing up and [16:47] we can try scientifically track that [16:48] with analytics so it's rightfully so [16:50] that someone's going to say okay we need [16:52] to provide a rich experience here to [16:54] make this as friction-free as possible [16:56] so developers say ok I need some kind of [16:58] client-side framework or at the very [17:00] least you know write a ton in JavaScript [17:01] so there are very good reasons to pick [17:04] up JavaScript and provide a good [17:06] experience and it's very easy to see [17:09] with just a simple example right if we [17:10] are trying to sign up for an application [17:12] I think by default almost all of us will [17:14] leave phone number blank because who [17:16] wants to provide a phone number [17:17] so we have to click Save and then oh it [17:19] can't be blank so we try to get into [17:20] your app but we couldn't so then we like [17:23] in or something bogus like no thanks RNA [17:25] at least this is my approach to most [17:27] application we hit save and like oh it's [17:29] not an actual phone number and you're [17:30] like oh gosh like ok I'm gonna enter a [17:33] fake phone number but it's actually [17:34] valid and then oh then most web [17:36] frameworks are going to actually try to [17:37] persist and tell me that the email has [17:39] already been taken so like this isn't [17:42] contrived like I've tried to sign up for [17:43] this app this 3 inputs and 4 times I the [17:47] users been rejected so like there are [17:50] valid reasons that people adopt this [17:52] complexity because they need to provide [17:54] a rich experience around this so I'm not [17:56] saying that javascript is just new and [17:58] shiny it's like people are using heavy [18:00] client-side apps to solve real business [18:02] problems and this is just a simple [18:03] example of one of those problems [18:07] but it's insane and I say that in a [18:10] nicest way possible because as this [18:13] example showed like just to go from a an [18:15] input tag to some rich user experience [18:17] around that is a labyrinth of decisions [18:20] potential arguments with teammates [18:21] learning just literally take an input [18:24] tag and then maybe show another label [18:26] error message beneath it it's absolute [18:28] insanity and but you have to decide okay [18:31] like if we're gonna use pure JavaScript [18:33] probably not [18:34] so what framework we're going to use is [18:35] gonna be ember angular react if we have [18:38] a debate we settle that with the team [18:40] maybe we've already learned one of these [18:41] great how are we gonna actually talk to [18:43] the server though we're gonna use Apollo [18:44] with graph QL because graph QL is an [18:47] awesome thing oK we've solved that [18:49] problem we have to then say okay how do [18:50] we actually compile this we're probably [18:52] gonna use webpack or there's a new one [18:54] now I forget the name but if we decide [18:56] that and we have to figure out what [18:57] loaders are we're going to use like [18:59] you're gonna be using Babel JSX if [19:01] you're using react not hard to learn but [19:03] then you're like Oh typescript you got [19:04] to say like half the tutorials that I've [19:06] went to have said type scripts the way [19:08] to go actually so now I'm learning like [19:09] a new language and type system just to [19:12] get a rich user experience around an [19:14] input tag that I already had written so [19:17] it's like what what in the world is [19:19] happening here and it can get even more [19:22] complex because let's say that you're [19:24] business person rightfully says like hey [19:27] this rich client side up actually [19:29] decreased our conversion rates because [19:31] we lost SCO because now we're rendering [19:34] all this on the client so you're like no [19:35] problem I can just start a node web [19:37] server in between the client app and my [19:40] in my actual web server and that thing [19:43] can actually render the HTML send it [19:45] back to the browser so BAM server [19:47] rendered HTML and I just rehydrate the [19:49] data and it's so it's insane I mean this [19:52] is for me like I said I'd say this a [19:54] nicest way I've been down this path work [19:56] I've worked for a lot of different [19:59] applications that there's so much [20:00] complexity here to accomplish the rich [20:02] user experience that we want and it may [20:05] be justified in some cases but I think [20:07] in a lot of cases if we had alternatives [20:09] we would all be better off for it and [20:11] better off in team size team bandwidth [20:14] time to market and just developer sanity [20:18] because I think a lot of us fall on a [20:21] spectrum of we're not trying to build a [20:23] desktop like experience so by all means [20:26] I'm not saying you can't write [20:27] JavaScript apps and client-side [20:29] applications are bad but I think a lot [20:30] of us want a somewhere between a basic [20:33] and rich user experience right we want [20:35] rich applications but we're not making [20:37] Google Maps or Google Docs or a Spotify [20:39] Web Player right there are certain [20:41] classes of applications where if you're [20:43] streaming audio and you want to view a [20:45] new page quote-unquote you can't cut the [20:47] audio stream load a new page and start [20:49] that again right so there are certainly [20:50] a class of applications that by all [20:52] means you need to go all in on this but [20:55] I think most of us at least most of the [20:56] applications I've built over the last [20:58] ten years working for a lot of different [21:00] clients we fall somewhere in between [21:02] right we don't want this basic HTML [21:04] we're gonna be using some JavaScript to [21:06] get these bits of active features and [21:10] that's where I think this feature is [21:11] going to be extremely well-suited so to [21:16] see how this works I want to take take a [21:17] trip into react if you don't know how to [21:20] react works it's okay because this is [21:21] their hello world example and this is [21:25] literally copy and pasted from the [21:26] reacts hello world yeah and I want to [21:28] talk about you know what is it that [21:29] makes client side applications special [21:31] why are they will able to provide [21:33] experiences that beat server-side [21:34] rendering well the goal is if we if we [21:37] start with a reacts example we can see [21:38] that they start with a pure operation [21:41] that looks remarkably like what you're [21:43] used to writing when you're rendering a [21:44] Phoenix view they take some stayed in [21:46] there's some data they call render it [21:49] spits out some HTML there's not a lot [21:50] happening here but this is our current [21:52] model that we're used to writing in in a [21:53] lik server with a Phoenix template we [21:56] have a render function on the view we [21:57] call it spits out HTML it's a pure [21:59] operation we're done so it looks [22:01] remarkably similar but the next step of [22:04] the react tutorial shows us you know [22:07] what what advantages we have for having [22:09] cloud running on the client we're able [22:11] to have a stateful component running on [22:13] the client right so in the react model [22:15] these components hold States and the key [22:17] thing that happens with react is anytime [22:19] they change their state they [22:21] automatically get rear-ended in the UI [22:23] updates so let me repeat that like in [22:25] this example we have a function that [22:27] gets called every second and all that [22:29] function does is change the component [22:31] State [22:31] and the react model is is my state [22:34] change render automatically gets [22:36] recalled and the up the UI automatically [22:38] gets updated so if you think about this [22:40] idea and you think about where I was [22:41] coming from in 2013 it's like okay we [22:44] have this super fast language Phoenix is [22:46] known for its crazy fast rendering and [22:48] we've got this great platform that [22:50] actually is great at stateful servers [22:52] and components like why can't we just [22:54] put this on the server we have a [22:55] platform extremely well suited to handle [22:57] event applications inhale holding state [23:01] so that's what we've done [23:03] so this is the react example that you [23:06] just saw ported ported to Phoenix live [23:08] you literally there's no tricks here [23:11] this is the whole code I didn't even [23:12] cheat by like taking off their imports [23:15] and in this case I'm using the Sybil II [23:17] which may look special but it's actually [23:20] always been part of Phoenix HTML it just [23:22] lets me write e^x directly and that gets [23:24] compiled to what your e^x 10 would [23:27] compile to so in this case I have a [23:30] render function that is still a pure [23:31] operation it takes some data in and it [23:33] produces HTML but now I have a stateful [23:36] view that is running it's spawned it's a [23:39] process that we're used to writing and [23:40] again this is using the react approach [23:42] where to get the clock from the hello [23:45] world example I just send myself a [23:46] message so a timer sent interval tick [23:48] every second I receive a ticket in and I [23:51] have a handle on foot callback that [23:52] we're all used to writing and all I do [23:54] is change my state and if my st. changes [23:57] render gets called and the UI updates so [24:00] the only difference is the UI updates [24:02] has a little bit of a network hop to go [24:05] over and a little bit of JavaScript [24:05] running but the conceptual model is the [24:08] same so let's see that in action I'll [24:13] bump up my font here so if I go to my [24:16] clock expand this so that we can see I [24:22] have a real-time application but every [24:26] second the view render function that you [24:29] just saw is called it pushes the update [24:32] down in the browser will talk about [24:34] optimizations for the future currently [24:36] it's pushing all the HTML back down but [24:39] we are doing a diff on the client so you [24:41] can see if you see the flash here in [24:43] chrome that h2 tag is the [24:45] we Dom element actually updating so [24:48] we're rendering the whole template on [24:49] the server every time data changes and [24:51] pushing it to the browser so pretty [24:54] pretty cool yeah thanks awesome so I [25:00] didn't expect the clap at this stage so [25:03] just wait just wait okay so the next the [25:10] next part of my talk was like so that [25:11] wasn't very exciting you know what makes [25:16] what makes client-side applications nice [25:19] and justifies the complexity is they're [25:21] interactive right it's not it's not just [25:23] a matter of the real-time updates coming [25:26] through like that's cool and useful but [25:28] usually the user is interacting with the [25:30] application right and that's what makes [25:32] it special and powerful so we have [25:34] Phoenix event bindings that allow [25:37] clients to interact with this staple [25:39] process on the server and it's super [25:41] simple [25:42] so in this hello world type example fits [25:45] on one slide I did cheat here I removed [25:47] the import I removed the use and I yeah [25:52] long story short I had to use their and [25:54] then I was a violating the elixir [25:55] formatter on the slide so I could make [25:58] sure my font was the same and then Joe's [25:59] they saw this and he's like why did you [26:01] anyway so I cheated with a used line to [26:05] accommodate Joe's a but this is [26:06] literally the only code that's required [26:08] to have client interaction so in this [26:10] example I'm just using straight HTML and [26:12] I have a Phoenix click and I want to [26:15] just increase and decrease encounter [26:16] let's say so I can say PHX click [26:18] decrement arbitrary event name and then [26:21] the server has a handle event Clause [26:23] that gets invoked on increment and [26:25] decrement and then all I do again is I [26:27] can update my state if I do nothing [26:29] nothing happens but if I one of my [26:31] callbacks changes in my state render [26:33] gets called and updates the UI so let's [26:36] check this out so if I go to slash [26:40] counter the count is 0 and if I click [26:45] plus and minus I have this staple [26:46] process out there up and running and [26:49] this thing could be doing anything it's [26:50] just a gen server underneath it's [26:51] holding state it's reacting to events in [26:54] this case as a Phoenix click event [26:57] so we have some type of interaction [26:59] model now and it Maps exactly to kind of [27:02] the flow that we're used to writing on [27:04] the client but it happens to be sending [27:06] the events over to the server and again [27:14] this is the model it's very simple it's [27:16] just the view produces HTML and then [27:20] we're used to that happening so HTML can [27:22] sit down to the clients and then with [27:25] these Phoenix bindings we're sending [27:26] these events back up to the server they [27:29] get hooked into a handle event so that [27:31] can do anything at once but if it [27:32] changes its state renders automatically [27:34] recalled pushed down to the browser and [27:36] this completes for as long as the user [27:37] is visiting the application so a very [27:40] simple model and to me it's very [27:42] powerful and super simple to reason [27:44] about but we can do more things than [27:47] just click on buttons and update [27:49] counters because to do anything useful [27:52] like client-side validations as I [27:54] mentioned that's a business problem that [27:56] would be nice to solve without bringing [27:57] in that labyrinth of complexity it'd be [27:59] nice to make that a like 30 second [28:02] process on the Phoenix IDE and we can do [28:05] that so this is the HTML that the [28:07] Phoenix scaffolds for you nothing [28:10] special for for we've all seen this like [28:12] wouldn't it be nice if we could just say [28:14] in exchange hey send this validate admit [28:17] to the server and form 4 isn't aware of [28:19] this Phoenix change event it's just a [28:21] data attribute and validates an [28:23] arbitrary event but it would be nice if [28:25] anytime any input event changed on this [28:27] form that we could just have a callback [28:29] wrote on the server so we can do that so [28:33] here is a Phoenix live view for a sign [28:35] up form on a knit we have a MP change [28:38] set so just like a new action and in [28:41] controller render is in this case is [28:44] just calling render of the view or the [28:46] template that we just looked at just a [28:48] normal pure Phoenix render and then we [28:51] have a handle of it validate and in this [28:53] case Phoenix passes us the serialized [28:56] form we get those form values and just [29:00] like we would do on the server normally [29:01] we pass those do a change set but in [29:04] this case we don't like check to see if [29:06] the changed is valid or render a [29:07] template we just add that change set to [29:09] our state [29:11] if it changed then it gets rear-ended [29:13] and pushed to the client and this gives [29:16] us immediate feedback and like literally [29:18] this is the only amount of code required [29:20] to get client-side style validations so [29:22] if you imagine if you have like the [29:24] requirement of localization support like [29:27] how would you get all of your get text [29:28] translations down to the clients like [29:30] they were maybe there's a way to do that [29:32] right but you have to like figure out [29:33] what payload to payload shape that takes [29:36] you gotta send it down to the client [29:37] somehow that's a ton of potentially a [29:38] ton of data on the wire just to add [29:40] internationalization so even if you have [29:42] a ton of experience getting client-side [29:45] validations in place there's still a lot [29:46] of complexity outside of the tooling [29:48] just to get that to work well and for us [29:50] it's no extra effort and if we want to [29:54] persist we can also use another binding [29:56] called a phoenix submit so in this case [29:58] instead of submitting to a controller i [30:00] can just submit back to that live view i [30:03] could submit the controller if I wanted [30:04] to but why submit all that input down to [30:07] the controller just to rerender if it [30:09] was in an air condition I already have [30:10] that up and running I should be able to [30:11] do it back in the process that I already [30:14] have running and in this case I just add [30:16] feeding submit save and then I define a [30:18] handle event save and this looks [30:21] remarkably like in controller and this [30:24] API is subject to change because we're [30:26] kind of mixing a controller a shapee eye [30:28] with a stateful api but the goal of this [30:31] is we should be able to very simply go [30:33] from this model that we're used to [30:34] running under write it in a live view [30:37] and now suddenly the application is ten [30:39] times more interactive and feature full [30:41] with potentially less code so in this [30:44] example it looks almost exactly like you [30:47] would see in a controller for the [30:48] successful condition we try to register [30:50] the user it worked [30:51] yay put the flash message redirect but [30:54] in this case you can actually see [30:55] redirect goes directly to another live [30:57] view which is kind of neat so you don't [30:59] have to redirect to a controller the [31:00] controller can completely fall away [31:03] entirely and you can just go directly to [31:04] another view and we'll see that in a [31:06] second and in the air condition this is [31:08] actually a simpler model than what [31:09] you're used to writing a controller the [31:10] controller would have to render like a [31:12] edit or new HTML template and pass the [31:14] state in but to me like this is even [31:16] simpler than that because we are already [31:18] rendering this template and anytime we [31:21] have an error we just change our state [31:22] we don't have to like do this branching [31:23] logic and say like Oh [31:24] now I actually have to render a template [31:27] here and what template in my end is like [31:28] no we just change our state so it [31:30] becomes much more data focused and for [31:32] me it's a simpler model than even the [31:34] credit controller code which is already [31:36] pretty nice and I think we could write [31:38] like imagine you could write mix Phoenix [31:40] gin live view and that did what the HTML [31:43] generator used but you had a real-time [31:45] application in less lines of code than [31:47] what it currently produces today and I [31:49] think we can absolutely do that and I [31:53] want to show you dimming with this so if [31:56] I go to my registration screen users [31:58] knew what you saw on my slides but much [32:04] better so looks like a normal form [32:07] because it is we sent HTML we rendered [32:09] it but there happens to be a process out [32:11] there running waiting for Phoenix [32:13] validate events and it turns out that [32:15] browsers the input forms or input fields [32:19] actually emit change events on keypress [32:21] so you're like okay no big deal on [32:23] keypress I'm going to validate so [32:25] nothing's happening here but imagine I [32:27] go back ok be blink so I type in Joe's a [32:30] and then oh must move letters and [32:32] numbers so I'm getting immediate [32:33] feedback and this is happening in real [32:36] time just on the server on every key [32:38] press because it looks amazing so the [32:42] user is getting this literally words [32:44] like you know the patient have to [32:45] refresh like it blows my mind how fast [32:46] this is and even over like when when I [32:49] was demoing this with Joe's a I was [32:51] using like I was exposing localhost you [32:53] know over the Atlantic Ocean to Poland [32:57] and it was like indistinguishable from a [33:00] local JavaScript application even from [33:02] my laptop Wi-Fi away Nick hop to Joe's a [33:05] so anyway it's just incredibly [33:07] compelling to write applications this [33:08] way and it works for every input so if I [33:10] want to type in an email address has to [33:13] be valid as soon as it becomes valid [33:14] goes away and again this is exactly the [33:18] normal form for change that you used to [33:20] writing there was nothing else added [33:22] other than the Phoenix bindings and [33:24] those callbacks that look remarkably [33:26] similar to the code that you're already [33:27] used to riding in a controller and again [33:30] for the phone number by tied to type no [33:32] thanks it would automatically tell me so [33:36] we're gonna save this [33:37] but before we save this I'm gonna open [33:39] up another tab because it turns out I [33:42] have a full crud view set up for all the [33:46] crud actions index view and show of you [33:50] am i go to my index view now the e^x is [33:56] exactly what you would get from a [33:58] Phoenix gen HTML so let's persist this [34:02] user have them register there's a new [34:05] flash message great you're used to [34:07] seeing this but we saw the new user [34:09] showed up on the left it's pretty nice [34:11] but it gets even better than that's [34:14] because I'm gonna go edit this user [34:15] there good a new page and say okay the [34:19] name is actually Jose a bloom way to [34:22] save Jose a and look o on the right it [34:24] updated and again this is just the code [34:27] that we're off already written and I'll [34:33] show you the show code in a second but [34:35] one of my favorite features too is I was [34:37] like wouldn't it be nice to just if I [34:39] delete a resource that that page knows [34:41] about it so I'm going to delete Jose a [34:43] now because he's he trolls me too much [34:45] I'm gonna perm perma banned him from [34:47] this app so I'm gonna delete Jose Jose [34:48] and that view redirected and put a flat [34:52] switches up like that users gone sorry [35:01] so pretty neat so here's my counter view [35:06] so I wasn't lying [35:07] that's all the code required so let's [35:09] check out that show of you [35:10] and again like I said the API is are new [35:14] here I'm rendering eex in line but that [35:17] could also be a regular template but if [35:19] we look at the code required to make [35:20] this fully real-time it's like what less [35:24] than 20 lines of code and in this case I [35:27] have an account skon text that uses [35:29] Phoenix pub/sub that just sends me a [35:31] message if this user gets changed or [35:33] deleted so the only thing that we have [35:35] to implement to make that a real-time [35:37] experience is any time I get a user [35:39] updated event I just refresh my data and [35:42] in this case I could just broadcast the [35:44] data but I'm gonna naively refetch it so [35:47] there's definitely some data [35:48] considerations here if you have a ton of [35:50] users on an app looking at a resource [35:52] you want to carefully not clear the [35:55] database for every user connected but [35:58] that's another problem and I've been [36:00] talking with a bin from absinthe on data [36:02] loader and how absence descriptions are [36:04] solving this issue but the simplest [36:06] approach is to just refresh our data if [36:09] the data change it rerender isn't up its [36:11] DUI and in the delete case this is just [36:14] the handle info that we're all used to [36:15] writing we get an event from the [36:16] accounts context oh this user was [36:18] deleted we just put a flash message and [36:20] redirect this shuts down and they're on [36:23] a new page so the model of this [36:26] real-time application is like I said [36:28] simpler than for me then the actual [36:30] credit HTML that we generate it's like [36:33] fewer files fewer conceptual leaps and [36:35] you get this real-time experience that [36:37] is insane [36:39] in my opinion so let's go back [36:47] and see some more examples so it's like [36:52] where's the limit here because we're not [36:54] quite sure where the boundary is I'm [36:56] like where does this break down I'll [36:57] talk about some caveats and where this [36:59] is really great and where we're not sure [37:01] this is great and cases where you [37:03] definitely wouldn't to use this but [37:06] we're not quite sure where you know [37:07] where the boundaries are on this out we [37:09] know that or this approach we're well [37:11] aware that there are certain use cases [37:12] like small bits of rich UI real-time [37:15] feedback like form validation showing [37:18] progress of something running in the [37:20] background you had a dashboard a UI [37:23] dashboard that you wanted to do have [37:25] periodic stats pushed down to you could [37:28] just render that on the server and have [37:30] a process waiting that information I [37:32] think this would be perfect for that [37:33] today as is there's a lot for us to [37:35] explore though because for example could [37:37] you implement an entire chat application [37:39] with this approach I'm not sure there's [37:42] a lot for us to figure out to make that [37:43] an actual valid approach so the goal is [37:46] to start small start four start with [37:48] simpler use cases that we know we can [37:50] solve really well and then work up to [37:52] where should you just bite the bullet [37:54] and take on the client complexity but I [37:57] think we can go a lot further than most [37:59] people think so for example if we take a [38:01] look at our JavaScript API this was on [38:03] Phoenix one three this is the one with [38:05] the race condition unfortunately we [38:07] tried to go with the pure functional API [38:09] and it turns out when you did have a [38:10] stateful API so our API got better [38:14] though so we had this was the old [38:15] JavaScript you had to write not bad the [38:18] new JavaScript got much nicer so it [38:19] fixed the race condition and got to [38:22] actually a very small amount of code [38:23] which is pretty nice but it would be [38:27] nice if we didn't have to write any [38:28] JavaScript right so let's see that so I [38:32] can have a UNIX presence backed [38:35] application and see what users are [38:37] online over feeis live you and there's [38:39] not any JavaScript required so let's see [38:41] what that looks like so now I'm back to [38:44] my index page on the Left I have an [38:46] anonymous user not too exciting yet but [38:50] let's bring up another user with a name [38:52] pris and now this just looks like I [38:56] wrote a Phoenix presents JavaScript [38:57] client but this is all server-side [38:58] rendered [38:59] then not write a single line of [39:00] JavaScript and in my e^x template it's [39:03] like for user and users like there's not [39:06] anything else there and let's open up [39:08] another tab and see like you know this [39:10] shows how many devices the user is [39:12] connected with so if I join from another [39:15] app you know instantaneously chris is [39:17] now on from two tabs so it's just as [39:20] fast as you would expect it to be if [39:21] you're writing a JavaScript app oh [39:22] there's one tab left and now chris has [39:25] left completely and he's gone completely [39:28] completely server-side rendered and it's [39:31] not a problem and all we have to do is [39:34] subscribe to a topic like we're used to [39:37] so in this example I just on an it asked [39:40] for my presence to be tracked and then I [39:43] subscribe to that topic to get events [39:45] when the user is join and leave I am [39:47] cheating here and that this is not an [39:49] efficient approach so it turns out I [39:51] wrote this feature and it works so well [39:53] that we actually like outran ourselves [39:56] because there exists no Phoenix presents [40:00] client that acts as a JavaScript client [40:03] that makes sense so phoenix tracker in [40:05] Phoenix presents replicates events but [40:07] we never had to actually write events [40:09] that stitches that data back together [40:10] because there was never a feature like [40:13] this so currently until I write that I [40:16] am just saying anytime the presents [40:19] information changes I just refresh all [40:20] the presences which you wouldn't want to [40:22] do for a large number of users but we [40:25] are going to now make in and what's the [40:26] client for that because we have a valid [40:28] use case for it but in a few lines of [40:30] server code I have Phoenix presents [40:32] features on a server rendered [40:34] application which is kind of ridiculous [40:41] but I know a lot of you were thinking [40:42] like key and this really scale like you [40:45] know clicksor is fast but like come on [40:47] like you know we have to pay the price [40:49] for rendering right so the answer is [40:52] like of course it's in scale because the [40:54] butcher and the platform that we're on [40:56] is amazing [40:57] but this is going to take resources [40:59] right so someone has to render the HTML [41:01] when you offload it to the client and [41:03] they're building that together so [41:05] pushing this to the server is absolutely [41:07] gonna spend more CPU cycles right we [41:09] can't defy the laws of physics and just [41:11] you know make up work and do it [41:13] somewhere outside of the server but we [41:16] can do it fast enough that it doesn't [41:18] matter like this is going to scale [41:19] completely well and to prove that to you [41:23] I have a nonsensical demo that actually [41:26] works so thanks to Joe's a actually so [41:30] we're doing Dom dishing on the client [41:33] and Jose was checking out this library [41:35] and he saw that the same authors that [41:37] wrote the client Dom diff wrote this [41:40] library called mark ojs which I had [41:43] never heard of but one of their examples [41:45] is pretty awesome you can't read the [41:47] font but this silky smooth animation is [41:51] not using CSS animations it's actually [41:55] like 100 div tags in every or at 60 [41:59] frames a second so 60 times a second [42:01] they are rendering 85 div tags and then [42:03] just setting a style attribute that [42:05] calculates the hue of the color and the [42:07] rotation and it's super like it's [42:09] impressive so they're showing like look [42:10] how fast our JavaScript library is at [42:13] rendering this so they wrote morph Tom [42:15] so morph time is super fast but Marco is [42:18] rendering this in like a Marco template [42:19] so they're trying to they're advertising [42:21] their template speed like look how fast [42:23] we are to be able to do this and it's [42:25] impressive so hands off to them and [42:27] actually thank you for writing more [42:28] thumb but josée was like you know what [42:30] why can't cook what if we tried this in [42:33] a look sir so we we got the elixir code [42:37] and I ported it to our sorry we got the [42:39] JavaScript code and I ported it to a [42:41] look sir and we really didn't know what [42:43] to expect here so let's go to that [42:45] rainbow view before we look at it so [42:49] this rainbow view is just a part of the [42:52] javascript code so this render rainbow [42:54] is just a one-to-one we went through and [42:57] we're just doing some calculations math [42:58] sign I honestly don't know what it's [43:00] doing but it's what the JavaScript was [43:01] doing and the goal is we produced a div [43:04] tag with the style attribute that sets [43:06] the color and it sets the CSS transform [43:08] but the CSS is not actually animating it [43:10] the animation is just from rear end [43:12] during so we were like well to get 60 [43:15] frames a second we'd have to timer soon [43:18] interval ourself a message every 16 and [43:21] 1/2 milliseconds like that seems really [43:23] fast and then we're like well we'll try [43:25] it so all we do with Phoenix live view [43:28] is we get an X frame event every 16 [43:31] milliseconds we change our state render [43:34] gets recalled and we push that to the [43:36] browser and we're like well I mean [43:37] that's that's the flow like let's see [43:41] what happens and I was yeah I would show [43:45] you the text between us but I can't [43:47] because there may be some expletives [43:50] when this actually was loaded so then I [43:54] was like well let's try this out so [43:57] that's rendering on the server at 60 [43:59] frames a second [44:05] so [44:09] so yeah I'm not saying use this for [44:12] animations but but can it scale yes it [44:17] can scale like it's it's a it's absurd [44:20] but that this worked and this is [44:22] actually not optimized at all so there's [44:24] there's a ton we can do like we're again [44:27] we're taking the Chris McCord approach [44:28] of like I just want this to work and [44:30] then like I'll worry about later how to [44:34] make it like great but but then it works [44:36] well enough that I can just ignore it [44:37] till Joe's days like we really can make [44:38] this much better [44:40] no but there's like there's an insane [44:42] amount of work that we're doing here but [44:43] we're taking i/o data we're actually [44:45] converting it to a binary and then we're [44:46] rendering that as Jason and we could [44:48] just take the i/o data and push it [44:50] straight down so like the fact that this [44:52] works so well as is is incredibly [44:54] compelling so I am really really excited [44:57] about this and the fact that this work [44:59] oh and the Marco example actually it [45:02] turns out when you click it it changes [45:04] direction and we were like wow we can do [45:06] that we have event my knees so you just [45:09] set a Phoenix click event and the [45:12] Phoenix click is gonna be Phoenix lick [45:14] switch and then I just changed my step [45:17] times negative one right like we have [45:19] this model and then so josy I was like [45:21] oh but it doesn't work for the click and [45:22] it was like three lines of code later [45:24] oh you change direction so it's just [45:27] been super fun to see this work I don't [45:30] know it's crazy so it's really exciting [45:35] so I'm really excited about this but I [45:38] do need to kind of like temper some [45:39] expectations here I mean one this is [45:41] really early but we know that it can [45:43] work really well for the use cases I [45:45] mentioned out of the box like [45:47] validations bits of real time UI but we [45:51] know for sure that you know we're not [45:52] trying to say don't write JavaScript [45:54] ever you know patient's absolutely that [45:57] you need to go on all in and use one of [45:58] the great frameworks available [45:59] especially if you need things like [46:01] offline support if you're building an [46:03] app that requires a server maybe not to [46:06] be there and then we can't be there so [46:08] sorry [46:08] you have to write JavaScript if you have [46:10] high latency clients I'm sure a lot of [46:12] you who are thinking oh this is great [46:13] but we have to go across the server now [46:15] for UI interaction so if you have edge [46:17] clients high latency clients then this [46:19] is not going to be a great option but I [46:20] think day to day most of [46:22] are operating on a client-server [46:24] architecture that requires a server to [46:26] be there in a reasonable amount of time [46:27] most of us that require any kind of [46:29] application that submits a form it does [46:31] something and the user waits for [46:32] feedback this will work perfectly fine [46:34] for but if you need optimistic UI where [46:37] you can't have latency then you have to [46:39] write code on the client if you want [46:41] native features complex UI a desktop [46:43] buyback like application then you need [46:45] to build code on the clients and that's [46:48] fine those what client those are what [46:50] client apps are great for but if you [46:52] want like great productivity or a great [46:54] time to market I think we're going to be [46:55] unbeatable in that regard like imagine [46:58] what I just showed for client side type [47:00] validations is literally like a 30 [47:02] second operation where even if you're a [47:04] professional JavaScript developer it [47:06] could be hours if you're new to the [47:08] ecosystem and you want your that [47:09] business requirement comes down that's [47:11] going to be days of work for you and we [47:12] can just ship this and go on to the next [47:15] task so I think time to market on a lot [47:17] of these rich features is going to be [47:19] unbeatable and I'm not aware of another [47:21] platform that has this kind of feature [47:23] set with the performance that we can [47:25] offer and again the latency feature to [47:28] is it's tough to use you have to [47:29] evaluate this on a case-by-case basis [47:31] because even if you have a highly latent [47:36] client if they have to download ten [47:37] megabytes of JavaScript like is that [47:38] gonna be better than server under HTML [47:41] that that shows up instantly but maybe [47:42] they have to wait a little bit for [47:44] interaction so we still have a lot of [47:45] wins on the client side because the [47:47] JavaScript to produce what you just saw [47:49] is like sixty lines of code so far I'm [47:51] sure that will grow as we add more [47:52] robustness and features but it's a [47:55] shockingly small payload and we get [47:57] things for free like SEO because it's [48:00] just server rendered HTML there's [48:01] nothing for us to worry about in that [48:03] regard because you go to a path on a web [48:06] page and you get HTML back so there's a [48:09] lot of wins here but again we're not [48:11] saying stop writing JavaScript for all [48:13] use cases [48:18] but we're able to do this because we're [48:20] able to render in microseconds and it's [48:22] been a great marketing feature for us so [48:24] Jason on the Phoenix core team was the [48:27] one that actually added the microsecond [48:31] character let's say I would have never [48:33] thought about it it would have been like [48:34] you know 0.4 in milliseconds and like I [48:37] think this is like this is the gift that [48:39] keeps on giving because people see this [48:40] like what's this micro character and [48:42] they're like oh my god [48:44] so it's amazing so this is like it's [48:47] amazing to see because it's it's really [48:49] enjoyable to write code that renders in [48:51] microseconds but it's amazing that like [48:54] people are blown away by this but this [48:56] has also led to people saying like well [48:59] like whoop-dee-doo like the user gets a [49:01] page in 40 milliseconds verse for 100 [49:04] microseconds like this is just about [49:05] bragging rights so there's some truth to [49:08] that so I'd say this is a great [49:09] marketing feature for us but it's it [49:12] needs to be about more than just us [49:14] being like woohoo we you know we had 400 [49:16] microseconds and you have milliseconds [49:18] like whoop-dee-doo so I want to talk [49:20] about this a little bit and say like you [49:22] know it's not just about performance for [49:25] us it's about more than that [49:27] and I read a blog post evaluating [49:29] different web framework performances and [49:31] they actually it was actually some [49:33] interesting points because I actually [49:35] agree with their first point they were [49:37] evaluating I believe in this case Ruby [49:40] first elixir and they said you know even [49:43] if it was faster would it matter like is [49:45] there such a thing as a fast enough web [49:47] application and I agree with this I do [49:50] think there is such a thing as a fast [49:51] enough web application if we're talking [49:53] about web applications that we've been [49:55] building up to the last decade [49:58] absolutely if a user gets a page in 40 [50:01] milliseconds verse four hundred [50:02] microseconds they're not going to notice [50:03] like it doesn't matter so I agree with [50:06] this point [50:06] but their next point is actually really [50:08] interesting because I said like just how [50:10] important is performance when choosing a [50:11] web framework or even a programming [50:13] language for a web application and to me [50:16] like it's incredibly important because [50:17] if you can choose a language that allows [50:20] you to advance the state of the art then [50:22] it makes all the difference so if we're [50:24] trying to build applications that we've [50:25] always built then it doesn't matter but [50:27] if we want to advance the state of the [50:28] art if we want to beat other competitors [50:31] market produced features faster than [50:32] them then absolutely the choice of [50:35] framework based on performance is [50:36] critical not because we can brag about [50:39] it it's because we can advance the state [50:40] of the art of web development and make [50:42] ourselves more productive so the [50:45] microsecond is going to stay there but [50:47] just realize it enables us to produce [50:49] incredible things not just to brag about [50:51] them even though we do brag so I'm [50:56] incredibly excited I'm going to add a [51:00] subheading to Josie's remarks earlier so [51:03] Jose Jose said you know the future is in [51:04] your hands as far as you know adopting a [51:07] lecture growing it yeah you know the [51:08] tools are there they looks for teams put [51:10] out the futures in your hands to shape [51:13] the rest of the language right if you [51:15] need a feature you're empowered to [51:16] implement it so I'm going to add on to [51:19] that and say you know not only is the [51:21] future in your hands the future is yours [51:23] to build like we have this platform that [51:25] can literally advance the state of the [51:27] art do things that other platforms [51:28] simply can't do so I want people to kind [51:31] of embrace this idea and kind of [51:33] challenge the status quo of what is [51:35] possible and what can we build now that [51:37] we have nerves now that we can actually [51:39] deploy devices in the field that are [51:41] running Phoenix itself are talking over [51:42] phoenix channels like the demo that [51:44] Justin gave uses phoenix channels on the [51:46] server and i completely blew my mind [51:48] that we were able to have devices just [51:50] up the update their firmware based on [51:52] getting a message from a server on the [51:54] backend like the future I think it's [51:56] ours to build and the more and more [51:58] markets we eat into I think the more [52:00] successful we're gonna be so I'm really [52:02] excited to see where this goes and thank [52:04] you for all coming out [52:16] and I'd be happy to answer questions if [52:19] we have time okay [52:21] are there any questions got one back [52:26] here go ahead [52:39] yeah so users state as far as the [52:42] clients yeah I have to repeat the [52:44] question so the question was about how [52:46] are we handling a user state because we [52:48] have a state on the client and as far as [52:50] I understand you're asking how do we [52:51] handle in the state that is on the [52:53] client given that we have stay also on [52:55] the server like [53:07] I say yes a question is also related to [53:09] like durability if you have a multi-step [53:10] wizard where you could this actually [53:12] wizards is a great way that this could [53:14] be used where you have like the user [53:16] completes a step and you just push [53:18] update new HTML but what happens if that [53:21] process crashes or the user leaves so [53:24] the great thing about this is all one [53:26] all the state is server-side so the [53:28] Phoenix change in Phoenix submit [53:32] bindings explicitly actually send the [53:34] entire serialize form exactly to kind of [53:37] help protect against having to do some D [53:39] balancing on like the server having the [53:40] input state and then the client changing [53:43] one input and then do the latency we [53:45] think that they had a previously valid [53:48] input so we the Phoenix change wraps the [53:50] whole form so we get the entire state so [53:53] the goal is to keep the state on the [53:54] server but then durability wise it's [53:56] gonna be a compound answer it's like [53:57] there's no durability but the nice thing [53:59] is like this is just a process on the [54:01] server so there's nothing special in [54:03] that you would handle durability the [54:05] exact same way you would handle [54:06] durability anywhere in your system when [54:08] it comes to a staple process so in the [54:10] in the wizard case at each step of the [54:12] way you could persist the step that way [54:14] you can restore that when they come back [54:16] and in that case you don't have to [54:18] persist everything but you may want to [54:20] or periodically you may just dump the [54:23] state right every few seconds you may [54:24] dump the state or you may treat it like [54:26] you would treat a normal controller [54:29] interaction where if you have to have [54:30] durability then you just persist at [54:32] every action that requires us so on next [54:34] you would persist that step the whole [54:36] step not just the whole input state not [54:38] just the single step so does that answer [54:40] the question got one over here [54:54] yeah absolutely so the question was [54:56] given any thought to coalescing so you [54:59] have the problem of 100 users are [55:03] viewing a fragment of HTML that it's all [55:06] gonna be rendered the same way yes we [55:08] put a lot of thought into it again this [55:09] is early so phoenix channels has this [55:12] solved it's called fast laning not [55:14] everyone may be aware of it so if you if [55:16] you broadcast on the channel today to a [55:19] chat application with a thousand users [55:21] it's gonna render that by default it [55:23] renders that payload of Jason a single [55:25] time instead of a hundred times because [55:28] it's the same thing you have to [55:29] explicitly say I'm gonna intercept this [55:31] event and customize it per client so we [55:33] are thinking about it today it would all [55:35] be rendered separately but it's [55:38] definitely something we want to solve [55:39] and like I said we've we've been [55:40] thinking about these things with [55:42] channels so I think there's a similar [55:44] model day that we can apply is that got [55:48] one back here okay yeah when can you [55:53] start playing with this pretty soon so [55:55] the nice thing is this exactly publish [55:58] this so it's not out yet but it is not [56:00] part of Phoenix course so I'll be able [56:02] to release it in a like point oh one [56:04] deal with it state and you know update [56:08] it up to 100 as fast as we want in [56:10] version and outside of Phoenix so I'm I [56:13] need to get Phoenix 1 for out first so [56:15] don't let me focus on something else and [56:17] then I can just claim like oh well I [56:18] promised it would be out in two weeks so [56:20] I can't really speak so no it should be [56:22] soon [56:23] just with the mindset that it's the [56:25] shape of the public interfaces and API [56:27] is gonna very well change back here [56:39] yes the question was if we if you had [56:42] for web servers running behind a load [56:44] balancer how would you determine where [56:45] the session is so it would just work so [56:49] fortunately Phoenix pub/sub is [56:50] distributed so that's not gonna be a [56:52] problem and it's gonna use WebSockets so [56:55] the clients could happen at load balance [56:56] to one server and it's gonna maintain [56:58] that connection we do reattach so yeah [57:02] they make a long story short there's a [57:04] likely change but there's a pig running [57:05] on the server somewhere and the client [57:07] is actually has a serialize token of [57:09] that pig so if you're using distributed [57:11] Erlang it will just work so it's likely [57:13] something you don't have to think about [57:15] in that regard got anyone else I can't [57:21] see with the lights [57:29] oh the code didn't work so Amazon it's [57:32] only on Prague Prague so if you go to [57:34] practice web page it works there but not [57:38] for Amazon so ya find me afterwards I [57:42] think Jim's got a couple of remarks so [57:44] thank you [57:45] [Applause]