0%

Domain experts may become more valuable, not less, in the AI era.

AI lowers the cost of software creation, but it does not lower the cost of knowing what should be built, what constraints matter, what data can be trusted, and what decisions are acceptable in a real operating environment. The bottleneck shifts from pure implementation to domain judgment.

Core idea

The winning person is not just a programmer and not just a subject matter expert. It is the domain expert who can use AI coding tools to turn proprietary workflows, internal knowledge, and company data into software.

This matters because:

  • Generic software knowledge is widely available.
  • Frontier models can generate decent code, UI, SQL, and integrations.
  • What remains scarce is context: internal processes, compliance rules, exceptions, edge cases, decision criteria, and access to the right data.

Why domain experts gain leverage

Before AI, many domain experts had ideas but could not build them quickly. They depended on engineering teams, budget cycles, and product prioritization. AI changes that.

With strong AI tools, a domain expert can:

  • prototype internal tools
  • generate dashboards and workflows
  • connect APIs and databases
  • encode decision rules
  • automate repetitive back-office work
  • create narrow software tailored to one team or company

This means the distance between “I know this process is broken” and “I built a working fix” becomes much shorter.

Where the moat comes from

The moat is usually not the code itself. The moat is the combination of:

  • proprietary data
  • operational context
  • domain-specific edge cases
  • trust from the business
  • understanding of which mistakes are expensive
  • access to workflows that outsiders cannot see

Anyone can ask AI to build an insurance claims workflow demo. Fewer people know how real claims teams handle exceptions, fraud signals, regulatory constraints, escalation policies, and messy legacy systems.

Examples

Healthcare

A doctor, clinic operator, or revenue-cycle specialist can use AI to build tools around:

  • prior authorization workflows
  • clinical documentation
  • billing review
  • coding assistance
  • patient triage

The hard part is not making forms. The hard part is understanding liability, reimbursement rules, and clinical risk.

Finance

A finance operator or analyst can build:

  • reconciliation workflows
  • internal reporting tools
  • portfolio monitoring
  • compliance checks
  • audit preparation systems

The advantage comes from knowing which controls matter and where bad data creates real risk.

A legal operations expert can create:

  • contract review pipelines
  • intake systems
  • clause libraries
  • obligation tracking
  • policy compliance tools

The value comes from judgment about acceptable language, review standards, and business risk.

Enterprise operations

An operations leader can automate:

  • approvals
  • vendor onboarding
  • support routing
  • QA checklists
  • internal knowledge systems

This is valuable because most enterprise friction lives in fragmented processes, not in the absence of software.

Implication for startups

Many future startups may be founded by domain experts with AI leverage rather than traditional software founders alone.

A likely pattern:

  1. A domain expert sees a painful workflow every day.
  2. They use AI to build an internal tool or workflow assistant.
  3. The tool becomes reliable enough for repeated use.
  4. Similar teams at other companies have the same problem.
  5. The internal tool becomes a product.

This is a strong path because the founder starts from real demand, not abstract feature ideas.

Implication for companies

Companies with strong proprietary data and strong domain operators should be able to produce much more software internally.

This could lead to:

  • more custom internal tools
  • more workflow automation
  • smaller teams shipping niche software
  • less dependence on large generic software vendors for every use case
  • more pressure on slow legacy vendors

The organizational challenge is that companies still need governance. If AI makes building easy, then review, security, and access control become more important.

Limitation

Being a domain expert is not enough by itself.

The best outcomes likely come from a combination of:

  • domain expertise
  • product taste
  • systems thinking
  • comfort with data
  • ability to validate outputs
  • enough technical fluency to guide AI well

A domain expert who blindly trusts AI can still create fragile or dangerous systems. Their advantage only holds if they can evaluate results and understand failure modes.

Career implication

This suggests an attractive profile for the future:

  • deep knowledge in one industry or function
  • strong ability to use AI tools
  • moderate technical fluency
  • ownership mindset around workflows and outcomes

Pure coding skill may become more commoditized. Pure business knowledge without execution may also weaken. The combination is where disproportionate value is created.

Strong thesis

AI does not eliminate the need for domain experts. It upgrades them into software producers.

The scarce resource is no longer only the ability to write code. It is the ability to convert real-world domain knowledge, proprietary context, and operational judgment into reliable automated systems.

In VueJS, data is reactive and mutatable – You do not need to call API to change the data like ReactJS does:

this.setState({count: 1})

Whereas in VueJS you just have to do it in a more natural way:

this.count = 1

But this convenience comes at a price: To do this magic thing, VueJS has to intercept your setter/getter in object but due to some limitation of Javascript core, it can not detect changes in the following:

  1. If you directly mutate array member, like array[1] = ‘text’
  2. If you do not declare the data at initialization phrase. That’s why data option should be a function returning object

To the first one, VueJS actually overrides some native array methods like push/pop/splice etc. Calling this methods will notify the watcher system so that UI will get update automatically. For the second one, however, it is possible that we are not able to know all the data available to our component at the time of initialization. To tackle this genuine scenario, VueJS offers set & delete.

These two APIs are both offered at global level and instance level. You can go to official documentation for the usage. It is simple

Vue.set(this, 'count', 1) //equivalent to this.count = 1
Vue.set(this.array, 1, 'text') //equivalent to this.array.splice(1, 1, 'text')

You can also check this Codepen for detail: https://codepen.io/frankdai/pen/VwppqVP Try to switch the JS code between the commented line and see the effect.

Above being said, in VueJS 3.0, its reactivity system moves from Object.defineProperty to Proxy as Javascript evolves with new bulit-in APIs. So set and delete are dropped in 3.0. It is recommend update from 2.x given your application has not much technical debt or you are building a brand new VueJS app.

render() vs. template

All VueJS developers are familiar with the template option. But sometimes render function comes more handy and flexible compared with template. However, not all the power build-in syntaxes offered by template are available in render function. So we will walk through some of the most common interpolate between the two ways of rendering UI in VueJS:

.sync modifier

Similar to React, VueJS props flow in top-to-bottom direction, or one-way data flow. You are not allowed to mutate props in child components directly. However VueJS does offer .sync modifier so that child component can emit event to let parent component to mutate the prop.

this.$emit('update:name', newName)

<parent>
<child :name.sync="myName"></child>
</parent>

From official document, we can easily know .sync modifier is just shorthand version of a combination of props (name) and event listener (@update:name). So we can adapt to our render function like this:


createElement('div', {
props: {
"name": this.myName
},
on: {
"update:name": ($event) => {
this.myName = $event;
}
}
})

v-model

Just like .sync modifier, v-model is another syntax sugar for to mimic the behavior of two-way data binding. Though most of the time developers use it for the input/select elements but it can also be applied to component level. It consists of two key things here: a prop named ‘value’ and an event named ‘input’. So basically we can translate v-model into options object as following:

return createElement(MyInput, { 
props: { value: this.message },
on: { input: (value) => { this.message = value } }
})

From this simple piece of code we can know that a component can also use v-model to pass a prop and allow its child to emit an input event with payload to mutate the prop named value.

.native modifier

If parent listens to a child component event, by default it expects the event should be emitted from child using $emit methods.

<child @click="onChildComponentClick" />

The onChildComponentClick callback will be triggered when child component emits an event named ‘click’.

However you might think of a click event as in browser sense. To achieve this, you can use @click.native to tell VueJS you want to register a native event listener on the root element of child component.

In official example, VueJS told us how to do this in render function as well:

 on: {
click: this.clickHandler
},
// For components only. Allows you to listen to
// native events, rather than events emitted from
// the component using `vm.$emit`.
nativeOn: {
click: this.nativeClickHandler
},

Scoped Slots

Although in template we only use but in component instance, we have two properties: $slots and $scopedSlots. So if we want to pass data to scoped slots in render function, we need to call $scopedSlots instead:

return createElement('div', [
this.$scopedSlots.default({
name: this.myName
})
])

this.$scopedSlots.default is a function which accepts a single argument and passes it to its slot as data.

v-bind

When we have to pass a lot of props to child component, it is very annoying to list all of them in the template, especially if there is an intermediate component whose sole purpose is to render some stateless UI and pass whatever the props from its parent to its children.

In template, we can easily achieve that by utilizing v-bind directives:

<child v-bind="options"></child>

With ES6 object spread syntax, we can adapt this to render function with a single line of code:

createElement('div', {
props: {
...this.options
}
})

Conclusion

In fact, all the markup in template will be parsed into render function for generating Virtual DOM. If you have any input, you are more than welcomed to post your comment.

Single File Component (SFC) in VueJS

A majority of VueJS developers should be using .vue file in their day-to-day development. Built by VueJS core team as community plugin, Vue Loader is the de factor build tools which works behind-the-scene and transform the Single File Component (SFC) into format recognized by browser. Its popularity derives mostly from two major offerings by this plugin:

Firstly, it provides more user-friendly version of writing component markup instead of built-in template option or render function, though latter can also takes up format of JSX like React.

Secondly, it also supports Scoped CSS to avoid the painstaking nature of global CSS namespace. So components will not affect each other with their CSS selector.

With these two distinctive features, you will be able to write logic (Javascript), markup (HTML) and style (CSS) in one single file. And today we will take a deep dive into Scoped SCSS:

How does it work?

CSS is of global namespace by nature. So in large scaled application we certainly want to avoid name conflicts when components are authored by different developers. So how does Vue Loader manage to keep CSS rules limited to the component itself?

In simple terms, when Vue Loader compiles each component, it will do a number of following things:

  1. When compiling <template> node, it will generate an unique id for each component and add to its HTML attribute like data-v-763db97b.

  2. When compiling <style> node, it will add this id to each CSS rule as attribute selector. So essentially if you have this simple CSS rule in .vue file

.red {
color:red
}

It will be transformed into

.red[data-v-763db97b] {
color:red
}

Vue Loader allows developer to write plain CSS or use preproceser like SCSS/LESS in the style node. After adding the attribute selector, these style nodes will be handled over to other loaders like style-loader and scss-loaders for next action. Developers can also configure post-css action in Webpack like minification, adding vendor prefix etc.

Let’s go to some of the details for Scoped CSS:

How to override a child component

Sometime we use a 3rd party component as child component and we want to override certain style. You write an rule in the parent component but it will not take effect in child, because the two components are of the different data-v-id. In this case, we will have to use /deep/ selector provided by Vue Loader:

/deep/ .child-component-class {
color: red
}

With the deep indicator, Vue Loader will transform it into different selector order like:

[data-v-763db97b] .child-component-class{
color:red
}

Compared with previous example, the order between class selector and attribute selector are swapped with an extra space between them. So browser will apply this to every child elements with this class name, including grandchildren component.

@import directives

If you are going to use SCSS/LESS in style node, Vue Loader will not be able to understand your code dependence. Each component will create its own context during pre-processing compilation. So if you have two components calling @import directives to include a common file for dependence so that you can use variable, mixin or call @extend, this file will be included into the final bundle for twice.

/*main.scss*/
.underline {
text-decoration: underline;
}
@import './common/main.scss'
.text {
@extend .underline;
}

In the final bundle, .underline class will appear twice in company with different attribute id selector. To avoid these duplication, we can move some of the common shared CSS code back to global namespace. It will be almost impossible to make entire app CSS scoped.

Root Element of Child Component

Vue Loader will add parent id into the root element of child

//parent component
<div class="root">
<div>Hello</div>
<child-component />
</div>
//child component
<div class="root">
<p>World</p>
</div>

If parent id is 123456 and child id is 654321, it will be complied to this:

<div class="root" data-v-123456> 
<div data-v-123456>Hello</div>
<div class="root" data-v-123456 data-v-654321>
<p data-v-654321>World</p>
</div>
</div>

So if you write an rule in parent component with .root selector, it will affect child component unintentionally.

Runtime Variable

This is not much related to SFC but we discuss this matter here as this is one of the most common requests. Imagine a service that allows its users to enter any hex value as color and change theme for personalization. How can we achieve that without overriding the entire CSS?

SCSS and LESS do let developers define variables but these are always build time. User’s color variable will be saved in server backend and has to be retrieved in runtime via Javascript code.

So we can use var function provided by browser. After calling API we will override global variable

getColor().then((color)=>{
var sheet = window.document.styleSheets[0];
sheet.insertRule(`:root { color: ${color}; }`, sheet.cssRules.length);
})

Conclusion

As above explanation, you will have a better comprehension of how Vue Loader Scoped CSS works and certain limitation with it. In the future we will talk about some alternatives scope CSS solutions like CSS-in-JS or CSS modules.

In React or VueJS, we often author components that are considered as layout components, which sets the generic layout UI like header/sidebar/footer etc so that they can be reused in different places. Each time a layout component is called, it does not care about what to be inserted into its children. In VueJS, we have Slots API and in React we have props.children to achieve that.

However, in certain scenarios we want to pass some data from parent into its children, even the parent does not know what kind of component will be of its children, given the fact that it is a generic layout component. How can we do so?

Let’s make a demo: The parent component will start counting how many seconds you have stayed in this web page (yep, that’s exactly what you saw in reactjs.org) and child component will consume this count and render it.

VueJS

We will resort what is referred as Scoped Slots


let TimeOnPage = {
template: `<div>
<slot v-bind:second="second" />
</div>`
data () {
return {second: 0}
},
mounted () {
setInterval(()=>this.second++, 1000)
}
}

let Child = {
template: `<time-on-page>
<template v-slot="slotProps">
{{slotProps.second}}
</template>
</time-on-page>`,
components: {TimeOnPage}
}

In the parent TimeOnPage we can use v-bind to bind an object to slot with structure like

slotProps = {
second: 0 //0 is reactive in child component this.second
}

so in child components can access this object. It is a common practice to name this object to slotProps in child v-slot directive but you can actually name it to whatever you would like.

React Hooks

With React Hooks we can easily share state between components by using customized hooks:

const useTimeOnPage = () => {
const [second, setSecond] = useState(0);
useEffect(() => {
setTimeout(() => {
setSecond(second + 1);
}, 1000);
}, [second]);
return second;
}
const Demo = () => {
const second = useTimeOnPage();
return <div>second</div>
}

However this is not exactly same as our topic, since we want to keep the data in the parent component and share in its immediate children. Here we can use a technique that is often referred as Render Props:

const Child = (props) => {
return <div>{props.second}s</div>;
};

const TimeOnPage = (props) => {
const [second, setSecond] = useState(0);
useEffect(() => {
setTimeout(() => {
setSecond(second + 1);
}, 1000);
}, [second]);
return <div>{props.render(second)}</div>;
};

<TimeOnPage render={(second) => <Child second={second} />

In this example, TimeOnPage will accept a function as props (The prop name can be called anything but usually we will name it as render) and pass its data as function arguments. The function will return a component which can access these arguments in this closure.