
- A dynamic environment, rapid decision-making, and a deep love for startups are what fuel my professional passion
- Worked as a sysadmin for 3 years - I have steel nerves and poker face)
Software Engineer at Blinkin.io
~10yrs of experience




First step - discovery
- 2-5 hours calls with C-level
- analyzing similar products (engineering blogs, articles)
- drawing schemas/prototypes (Miro)
- Functional and Non-functional requirements
# CHAPTER 1



Research
# CHAPTER 2
Node-based Editor
Realtime Collaboration
Drag&Drop & Resize & Rotate
MultiSelection & Grouping
Powerful inline Text/Image Editor
Themes & Templates
Tenant-based architecture
Data governence
Preview inside Editor
Publish
Ctrl + Z
History
ChatGPT / AI / ML
Forms
Integrations
HTML vs Canvas
PDF -> Guide
Platforms/Services
# CHAPTER 2


Next step - Validate decisions
# CHAPTER 3

Time - 1 month
... and create ADRs
# CHAPTER 3

Architectural Decision Record
Now let's make this ship happen!
General Overview
# CHAPTER 4

React app structure
# CHAPTER 5
Feature-Sliced Design + Atomic Design

Entity - "Component"
(Segmentation)
# CHAPTER 5

Hierarchy
# CHAPTER 5

BE-FE communication
# CHAPTER 6
Incremental change update

State Management - 1 part
# CHAPTER 7

State Management - 2 part
# CHAPTER 7

- Caching
- Updating "out of date" data in the background
- Managing memory - revalidate, refetch by Query Keys
- Make your application more maintainable
- Potentially help you save on bandwidth
State Management - ReactQuery
# CHAPTER 7
const useFonts = useQuery({
queryKey: [QUERY_KEYS.FONTS],
queryFn: async () => FontsAPI.fetchFonts().then(d => data.data)
});
const { data, isLoading } = useFonts();
....
export const useAddNewFontMutation = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (payload: AddNewFontRequest) => FontsAPI.addNewFont(payload),
onSuccess: () => queryClient.invalidateQueries([
QUERY_KEYS.FONTS
]);
});
}
UUID v4 as fundamental attribute
# CHAPTER 8

Text Editing
# CHAPTER 10

Made by Facebook
An extensible text editor framework

Reactflow
# CHAPTER 11
for building node-based editors

Online collaboration with Yjs
# CHAPTER 12
Shared editing framework
Offline Support
Network agnostic (Peer-to-Peer)
CRDT implementation
Shared cursors
CRDT
# CHAPTER 12

Conflict-free
Replicated
Data
Types
=
W
T
F
CRDT in Yjs
# CHAPTER 12

Example
import * as Y from 'yjs';
const ydoc = new Y.Doc();
const nodesMap = ydoc.getMap<Node>('nodes');
function useNodesStateSynced(): [Node[], OnNodesChange] {
const [nodes, setNodes] = useState<Node[]>([]);
const onNodesChanges = useCallback((changes: NodeChange[]) => {});
useEffect(() => {
const observer = () => setNodes(Array.from(nodesMap.values()));
setNodes(Array.from(nodesMap.values()));
nodesMap.observe(observer);
return () => nodesMap.unobserve(observer);
}, [setNodes]);
return [nodes, onNodesChanges];
}
# CHAPTER 12
Floating / Smart positioning
# CHAPTER 13
Tree-shakeable & Platform-agnostic
Smart Anchor Positioning

Floating / Smart positioning
# CHAPTER 13

const { refs, update, floatingStyles } = useFloating({
placement: placement ?? 'top',
open: opened,
onOpenChange: setOpened,
middleware: [
flip({
fallbackAxisSideDirection: 'end',
fallbackStrategy: 'bestFit',
fallbackPlacements: ['top', 'bottom', 'left', 'right'],
}),
offset(15)
],
});
Drag/Resize/Rotate/Snap/MultiSelect
# CHAPTER 14

# CHAPTER 14

User Buffer
# CHAPTER 13

User Buffer
# CHAPTER 13
const data = {
components: [{
"id": "98268bd4-568c-4a5a-8751-109e6ff05ef8",
"data": {
"media": { id: "..", path: "https://example.com/photos/2280547.jpeg" },
"order": 0,
},
...
}],
};
// Convert all medias to Base64
toBase64(data.components.media);
navigator.clipboard.writeText(createClipboardDataFromJSON(data));
// {
// type: "data/clipboard",
// payload: { components: [...] }
// }
Testing
# CHAPTER 15

"Simplicity is not the state of when there is nothing more to add, but the state when there is nothing more to take away."
(с)Antoine de Saint-Exupéry

In the Lab: Crafting the No-{Code} Editor POC
By Oleksandr Maliuta
In the Lab: Crafting the No-{Code} Editor POC
- 176