PageSourceSearch

https://prettywhale.ai/

html prettywhale.ai collected 2026-10-05 14:06:42 UTC 219,945 bytes, 1,355 lines download raw bytes

1<!DOCTYPE html><html lang="en"><head>
2  <meta charset="UTF-8">
3  <link rel="icon" type="image/png" href="/favicon.png">
4  <meta name="viewport" content="width=device-width, initial-scale=1.0">
5  <meta name="description" content="PrettyWhale.ai generates your data pipelines with its tests, quality checks, schemas, orchestration and docs. Production-ready, portable, yours to deploy.">
6  <link rel="canonical" href="https://prettywhale.ai/">
7
8  <!-- Open Graph -->
9  <meta property="og:title" content="Generate Data Pipelines You Trust | PrettyWhale.ai">
10  <meta property="og:description" content="PrettyWhale.ai generates your data pipelines with its tests, quality checks, schemas, orchestration and docs. Production-ready, portable, yours to deploy.">
11  <meta property="og:url" content="https://prettywhale.ai/">
12  <!-- Raster only: social platforms drop image/svg+xml. Kept in sync with OG_IMAGE in seo.service.ts. -->
13  <meta property="og:image" content="https://prettywhale.ai/assets/og-default.png">
14  <meta property="og:image:width" content="1200">
15  <meta property="og:image:height" content="630">
16  <meta property="og:image:alt" content="PrettyWhale.ai — ingestion code you trust">
17  <meta property="og:type" content="website">
18  <meta property="og:site_name" content="PrettyWhale.ai">
19
20  <!-- Twitter Card -->
21  <meta name="twitter:card" content="summary_large_image">
22  <meta name="twitter:title" content="Generate Data Pipelines You Trust | PrettyWhale.ai">
23  <meta name="twitter:description" content="PrettyWhale.ai generates your data pipelines with its tests, quality checks, schemas, orchestration and docs. Production-ready, portable, yours to deploy.">
24  <meta name="twitter:image" content="https://prettywhale.ai/assets/og-default.png">
25  <meta name="twitter:image:alt" content="PrettyWhale.ai — ingestion code you trust">
26
27  <title>Generate Data Pipelines You Trust | PrettyWhale.ai</title>
28
29  <!-- Structured data (Organization + WebSite) -->
30  
30<script type="application/ld+json">
31      {
32        "@context": "https://schema.org",
33        "@graph": [
34          {
35            "@type": "Organization",
36            "@id": "https://prettywhale.ai/#organization",
37            "name": "PrettyWhale.ai",
38            "alternateName": "PrettyWhale",
39            "legalName": "PrettyWhale.ai",
40            "url": "https://prettywhale.ai",
41            "logo": "https://prettywhale.ai/assets/logo-pw.png",
42            "description": "Engineering AI that generates data ingestion and integration code (complete data pipelines), with its tests, quality checks, schemas, orchestration and documentation.",
43            "email": "[email protected]",
44            "vatID": "FR25994801223",
45            "identifier": {
46              "@type": "PropertyValue",
47              "name": "SIRET",
48              "value": "99480122300011"
49            },
50            "address": {
51              "@type": "PostalAddress",
52              "streetAddress": "49 rue de Ponthieu",
53              "postalCode": "75008",
54              "addressLocality": "Paris",
55              "addressCountry": "FR"
56            },
57            "sameAs": ["https://www.linkedin.com/company/prettywhale-ai/"],
58            "knowsAbout": [
59              "Data ingestion",
60              "Data integration",
61              "Data pipelines",
62              "Data quality",
63              "Code generation",
64              "Small language models",
65              "Engineering AI"
66            ]
67          },
68          {
69            "@type": "WebSite",
70            "@id": "https://prettywhale.ai/#website",
71            "name": "PrettyWhale.ai",
72            "alternateName": "PrettyWhale",
73            "url": "https://prettywhale.ai",
74            "publisher": { "@id": "https://prettywhale.ai/#organization" }
75          }
76        ]
77      }
78    </script>
78
79
80  <link rel="preconnect" href="https://fonts.googleapis.com">
81  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="">
82  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&amp;family=Poppins:wght@300;400;500;600;700;800&amp;family=JetBrains+Mono:wght@400;500&amp;display=swap" rel="stylesheet">
83  
83<script type="module" crossorigin="" src="/assets/index-BZ4Q57Vd.js"></script>
83
84  <link rel="stylesheet" crossorigin="" href="/assets/index-DtTAeYv8.css">
85<style>.header[_ngcontent-ng-c1321049901] {
86  position: sticky;
87  top: 0;
88  z-index: 100;
89  background-color: var(--surface-translucent);
90  backdrop-filter: blur(12px);
91  border-bottom: 1px solid var(--border-alt);
92}
93.header[_ngcontent-ng-c1321049901]   .inner[_ngcontent-ng-c1321049901] {
94  display: flex;
95  align-items: center;
96  justify-content: space-between;
97  height: var(--header-height);
98}
99.header[_ngcontent-ng-c1321049901]   .logo[_ngcontent-ng-c1321049901] {
100  display: flex;
101  align-items: center;
102  gap: var(--spacing-sm);
103  z-index: 1;
104}
105.header[_ngcontent-ng-c1321049901]   .logo-text[_ngcontent-ng-c1321049901] {
106  font-family: var(--font-inter);
107  font-size: 17px;
108  font-weight: 600;
109  color: var(--tertiary);
110}
111.header[_ngcontent-ng-c1321049901]   .logo-accent[_ngcontent-ng-c1321049901] {
112  color: var(--primary);
113}
114.header[_ngcontent-ng-c1321049901]   .nav[_ngcontent-ng-c1321049901] {
115  display: flex;
116  align-items: center;
117  gap: var(--spacing-xs);
118}
119.header[_ngcontent-ng-c1321049901]   .link[_ngcontent-ng-c1321049901] {
120  font-size: var(--font-size-nav);
121  color: var(--on-surface);
122  transition: color var(--transition-fast), background-color var(--transition-fast);
123  background: none;
124  border: 4px solid transparent;
125  cursor: pointer;
126  font-family: inherit;
127  display: inline-flex;
128  align-items: center;
129  justify-content: center;
130  gap: 4px;
131  padding: var(--spacing-xs) var(--spacing-sm);
132  border-radius: var(--border-radius-xs);
133  margin: 0;
134  white-space: nowrap;
135}
136.header[_ngcontent-ng-c1321049901]   .link[_ngcontent-ng-c1321049901]:hover {
137  color: var(--primary);
138}
139.header[_ngcontent-ng-c1321049901]   .link[_ngcontent-ng-c1321049901]:focus-visible {
140  outline: 2px solid var(--border-focus);
141  outline-offset: 2px;
142}
143.header[_ngcontent-ng-c1321049901]   .resources[_ngcontent-ng-c1321049901] {
144  position: relative;
145}
146.header[_ngcontent-ng-c1321049901]   .resources-heading[_ngcontent-ng-c1321049901] {
147  display: none;
148}
149.header[_ngcontent-ng-c1321049901]   .icon-chevron[_ngcontent-ng-c1321049901], 
150.header[_ngcontent-ng-c1321049901]   .icon-editor[_ngcontent-ng-c1321049901], 
151.header[_ngcontent-ng-c1321049901]   .icon-arrow[_ngcontent-ng-c1321049901] {
152  background-color: currentColor;
153  mask-size: contain;
154  mask-repeat: no-repeat;
155  mask-position: center;
156}
157.header[_ngcontent-ng-c1321049901]   .icon-chevron[_ngcontent-ng-c1321049901] {
158  width: 12px;
159  height: 12px;
160  mask-image: url("/assets/icons/chevron-right.svg");
161  rotate: 90deg;
162  transition: rotate var(--transition-fast);
163}
164.header[_ngcontent-ng-c1321049901]   .icon-editor[_ngcontent-ng-c1321049901] {
165  width: 15px;
166  height: 15px;
167  mask-image: url("/assets/icons/vscode.svg");
168}
169.header[_ngcontent-ng-c1321049901]   .icon-arrow[_ngcontent-ng-c1321049901] {
170  width: 12px;
171  height: 12px;
172  mask-image: url("/assets/icons/arrow-diagonal.svg");
173}
174.header[_ngcontent-ng-c1321049901]   .resources.open[_ngcontent-ng-c1321049901]   .icon-chevron[_ngcontent-ng-c1321049901] {
175  rotate: -90deg;
176}
177.header[_ngcontent-ng-c1321049901]   .resources-menu[_ngcontent-ng-c1321049901] {
178  position: absolute;
179  top: calc(100% + var(--spacing-2xs));
180  left: 0;
181  display: none;
182  flex-direction: column;
183  min-width: 180px;
184  padding: var(--spacing-xs);
185  border-radius: var(--border-radius-sm);
186  background-color: var(--surface-container);
187  box-shadow: 0 6px 6px rgba(0, 0, 0, 0.25);
188  list-style: none;
189}
190.header[_ngcontent-ng-c1321049901]   .resources.open[_ngcontent-ng-c1321049901]   .resources-menu[_ngcontent-ng-c1321049901] {
191  display: flex;
192}
193.header[_ngcontent-ng-c1321049901]   .resources-link[_ngcontent-ng-c1321049901] {
194  justify-content: flex-start;
195  width: 100%;
196  padding: var(--spacing-sm) var(--spacing-md);
197  border: none;
198  white-space: nowrap;
199}
200.header[_ngcontent-ng-c1321049901]   .install[_ngcontent-ng-c1321049901], 
201.header[_ngcontent-ng-c1321049901]   .contact[_ngcontent-ng-c1321049901] {
202  font-size: var(--font-size-sm);
203  font-weight: 600;
204  white-space: nowrap;
205  padding: var(--spacing-xs) var(--spacing-md);
206  border-radius: var(--border-radius-xs);
207  transition: opacity var(--transition-fast);
208}
209.header[_ngcontent-ng-c1321049901]   .install[_ngcontent-ng-c1321049901]:hover, 
210.header[_ngcontent-ng-c1321049901]   .contact[_ngcontent-ng-c1321049901]:hover {
211  opacity: 0.9;
212}
213.header[_ngcontent-ng-c1321049901]   .install[_ngcontent-ng-c1321049901] {
214  display: inline-flex;
215  align-items: center;
216  gap: var(--spacing-2xs);
217  border: 4px solid var(--primary-border);
218  background-color: var(--primary);
219  color: var(--const-white);
220}
221.header[_ngcontent-ng-c1321049901]   .contact[_ngcontent-ng-c1321049901] {
222  border: 4px solid var(--secondary-border);
223  background-color: var(--secondary);
224  color: var(--on-surface);
225}
226.header[_ngcontent-ng-c1321049901]   .burger[_ngcontent-ng-c1321049901] {
227  display: none;
228  flex-direction: column;
229  justify-content: center;
230  gap: 6px;
231  width: 32px;
232  height: 32px;
233  padding: 4px;
234  background: none;
235  border: none;
236  cursor: pointer;
237  z-index: 1;
238}
239.header[_ngcontent-ng-c1321049901]   .burger-line[_ngcontent-ng-c1321049901] {
240  display: block;
241  width: 100%;
242  height: 3px;
243  background-color: var(--on-surface);
244  border-radius: 1px;
245  transition: transform 0.3s, opacity 0.3s;
246}
247.header[_ngcontent-ng-c1321049901]   .burger[aria-expanded=true][_ngcontent-ng-c1321049901]   .burger-line[_ngcontent-ng-c1321049901]:nth-child(1) {
248  transform: translateY(9px) rotate(45deg);
249}
250.header[_ngcontent-ng-c1321049901]   .burger[aria-expanded=true][_ngcontent-ng-c1321049901]   .burger-line[_ngcontent-ng-c1321049901]:nth-child(2) {
251  opacity: 0;
252}
253.header[_ngcontent-ng-c1321049901]   .burger[aria-expanded=true][_ngcontent-ng-c1321049901]   .burger-line[_ngcontent-ng-c1321049901]:nth-child(3) {
254  transform: translateY(-9px) rotate(-45deg);
255}
256@media (max-width: 768px) {
257  .header[_ngcontent-ng-c1321049901] {
258    transition: transform 0.3s ease;
259  }
260  .header.hidden[_ngcontent-ng-c1321049901] {
261    transform: translateY(-100%);
262  }
263  .header[_ngcontent-ng-c1321049901]   .burger[_ngcontent-ng-c1321049901] {
264    display: flex;
265  }
266  .header[_ngcontent-ng-c1321049901]   .nav[_ngcontent-ng-c1321049901] {
267    position: fixed;
268    top: var(--header-height);
269    left: 0;
270    right: 0;
271    flex-direction: column;
272    align-items: stretch;
273    gap: 0;
274    background-color: var(--surface);
275    border-bottom: 1px solid var(--border);
276    padding: var(--spacing-sm) 0;
277    display: none;
278  }
279  .header[_ngcontent-ng-c1321049901]   .nav.visible[_ngcontent-ng-c1321049901] {
280    display: flex;
281    opacity: 0;
282    transform: translateY(-8px);
283    transition: opacity 0.3s, transform 0.3s;
284  }
285  .header[_ngcontent-ng-c1321049901]   .nav.visible.open[_ngcontent-ng-c1321049901] {
286    opacity: 1;
287    transform: translateY(0);
288  }
289  .header[_ngcontent-ng-c1321049901]   .link[_ngcontent-ng-c1321049901] {
290    font-size: var(--font-size-base);
291    margin: 0;
292    padding: var(--spacing-sm) var(--spacing-lg);
293    border: none;
294    border-radius: 0;
295    justify-content: flex-start;
296  }
297  .header[_ngcontent-ng-c1321049901]   .link[_ngcontent-ng-c1321049901]:hover {
298    background-color: var(--surface-container);
299  }
300  .header[_ngcontent-ng-c1321049901]   .resources-toggle[_ngcontent-ng-c1321049901] {
301    display: none;
302  }
303  .header[_ngcontent-ng-c1321049901]   .resources-heading[_ngcontent-ng-c1321049901] {
304    display: block;
305    padding: var(--spacing-sm) var(--spacing-lg) var(--spacing-2xs);
306    font-size: var(--font-size-xs);
307    font-weight: 600;
308    letter-spacing: 0.08em;
309    text-transform: uppercase;
310    color: var(--on-surface-container);
311  }
312  .header[_ngcontent-ng-c1321049901]   .resources-menu[_ngcontent-ng-c1321049901] {
313    position: static;
314    display: flex;
315    min-width: 0;
316    padding: 0;
317    border-radius: 0;
318    background: none;
319    box-shadow: none;
320  }
321  .header[_ngcontent-ng-c1321049901]   .resources-link[_ngcontent-ng-c1321049901] {
322    padding-left: var(--spacing-2xl);
323  }
324  .header[_ngcontent-ng-c1321049901]   .install[_ngcontent-ng-c1321049901] {
325    justify-content: center;
326    margin: var(--spacing-sm) var(--spacing-lg) 0;
327  }
328  .header[_ngcontent-ng-c1321049901]   .contact[_ngcontent-ng-c1321049901] {
329    margin: var(--spacing-sm) var(--spacing-lg);
330    text-align: center;
331  }
332}</style><style>.hero[_ngcontent-ng-c2863248315] {
333  position: relative;
334  min-height: calc(100vh - var(--header-height));
335  padding: var(--spacing-4xl) 0 var(--spacing-3xl);
336  background: var(--surface);
337  cursor: default;
338}
339.hero[_ngcontent-ng-c2863248315]   .inner[_ngcontent-ng-c2863248315] {
340  position: relative;
341  z-index: 1;
342  display: grid;
343  grid-template-columns: 1fr 1fr;
344  gap: var(--spacing-2xl);
345  align-items: center;
346  min-height: calc(840px - var(--spacing-4xl) - var(--spacing-3xl));
347}
348.hero[_ngcontent-ng-c2863248315]   .title[_ngcontent-ng-c2863248315] {
349  font-size: var(--font-size-4xl);
350  font-weight: 700;
351  line-height: var(--line-height-tight);
352  margin-bottom: var(--spacing-lg);
353  color: var(--tertiary);
354}
355.hero[_ngcontent-ng-c2863248315]   .gradient[_ngcontent-ng-c2863248315] {
356  background: linear-gradient(90deg, var(--linear-1), var(--linear-2));
357  background-clip: text;
358  -webkit-background-clip: text;
359  -webkit-text-fill-color: transparent;
360}
361.hero[_ngcontent-ng-c2863248315]   .subtitle[_ngcontent-ng-c2863248315] {
362  font-family: var(--font-inter);
363  font-size: var(--font-size-lg);
364  color: var(--tertiary);
365  font-weight: 500;
366  max-width: 100%;
367  margin-bottom: var(--spacing-xl);
368}
369.hero[_ngcontent-ng-c2863248315]   .actions[_ngcontent-ng-c2863248315] {
370  display: flex;
371  align-items: center;
372  gap: var(--spacing-sm);
373}
374.hero[_ngcontent-ng-c2863248315]   .install-link[_ngcontent-ng-c2863248315] {
375  display: inline-flex;
376  align-items: center;
377  gap: var(--spacing-xs);
378  white-space: nowrap;
379  padding: var(--spacing-sm) var(--spacing-lg);
380  border-radius: var(--border-radius-sm);
381  border: 4px solid var(--primary-border);
382  background-color: var(--primary);
383  color: var(--const-white);
384  font-weight: 600;
385  font-size: var(--font-size-base);
386  text-decoration: none;
387  transition: opacity var(--transition-fast);
388}
389.hero[_ngcontent-ng-c2863248315]   .install-link[_ngcontent-ng-c2863248315]:hover {
390  opacity: 0.9;
391}
392.hero[_ngcontent-ng-c2863248315]   .contact-link[_ngcontent-ng-c2863248315] {
393  display: inline-flex;
394  align-items: center;
395  white-space: nowrap;
396  padding: var(--spacing-sm) var(--spacing-lg);
397  border-radius: var(--border-radius-sm);
398  border: 4px solid var(--secondary-border);
399  background-color: var(--secondary);
400  color: var(--on-surface);
401  font-weight: 600;
402  font-size: var(--font-size-base);
403  text-decoration: none;
404  transition: opacity var(--transition-fast);
405}
406.hero[_ngcontent-ng-c2863248315]   .contact-link[_ngcontent-ng-c2863248315]:hover {
407  opacity: 0.9;
408}
409.hero[_ngcontent-ng-c2863248315]   .icon-arrow[_ngcontent-ng-c2863248315], 
410.hero[_ngcontent-ng-c2863248315]   .icon-editor[_ngcontent-ng-c2863248315] {
411  display: inline-block;
412  width: 16px;
413  height: 16px;
414  background-color: currentColor;
415  mask-size: contain;
416  mask-repeat: no-repeat;
417  mask-position: center;
418}
419.hero[_ngcontent-ng-c2863248315]   .icon-arrow[_ngcontent-ng-c2863248315] {
420  mask-image: url("/assets/icons/arrow-diagonal.svg");
421}
422.hero[_ngcontent-ng-c2863248315]   .icon-editor[_ngcontent-ng-c2863248315] {
423  width: 18px;
424  height: 18px;
425  mask-image: url("/assets/icons/vscode.svg");
426}
427.hero[_ngcontent-ng-c2863248315]   .visual[_ngcontent-ng-c2863248315] {
428  display: flex;
429  justify-content: center;
430}
431.hero[_ngcontent-ng-c2863248315]   .visual[_ngcontent-ng-c2863248315]   img[_ngcontent-ng-c2863248315] {
432  width: 100%;
433  height: auto;
434  filter: drop-shadow(-10px 16px 9px rgba(0, 0, 0, 0.25));
435}
436@media (max-width: 768px) {
437  .hero[_ngcontent-ng-c2863248315]   .inner[_ngcontent-ng-c2863248315] {
438    grid-template-columns: 1fr;
439  }
440  .hero[_ngcontent-ng-c2863248315]   .title[_ngcontent-ng-c2863248315] {
441    font-size: var(--font-size-xxl);
442  }
443  .hero[_ngcontent-ng-c2863248315]   .actions[_ngcontent-ng-c2863248315] {
444    flex-direction: column;
445    align-items: flex-start;
446  }
447}</style><style>.demo-section[_ngcontent-ng-c312145568] {
448  padding: var(--spacing-5xl) 0;
449  background-color: var(--surface-container);
450}
451.demo-section[_ngcontent-ng-c312145568]   .heading[_ngcontent-ng-c312145568] {
452  margin-bottom: var(--spacing-2xl);
453}
454.demo-section[_ngcontent-ng-c312145568]   .title[_ngcontent-ng-c312145568] {
455  font-size: var(--font-size-3xl);
456  color: var(--on-surface);
457  text-align: left;
458  margin-bottom: var(--spacing-md);
459}
460.demo-section[_ngcontent-ng-c312145568]   .subtitle[_ngcontent-ng-c312145568] {
461  font-family: var(--font-inter);
462  font-size: var(--font-size-lg);
463  font-weight: 400;
464  line-height: var(--line-height-relaxed);
465  color: var(--on-surface-container);
466  text-wrap: pretty;
467}
468.demo-section[_ngcontent-ng-c312145568]   .subtitle[_ngcontent-ng-c312145568]   strong[_ngcontent-ng-c312145568] {
469  font-weight: 600;
470  color: var(--on-surface);
471}
472.demo-section[_ngcontent-ng-c312145568]   .preview[_ngcontent-ng-c312145568]   video[_ngcontent-ng-c312145568] {
473  width: 100%;
474  display: block;
475  border-radius: var(--border-radius-md);
476}
477@media (max-width: 768px) {
478  .demo-section[_ngcontent-ng-c312145568]   .heading[_ngcontent-ng-c312145568] {
479    margin-bottom: var(--spacing-xl);
480  }
481  .demo-section[_ngcontent-ng-c312145568]   .subtitle[_ngcontent-ng-c312145568] {
482    font-size: var(--font-size-base);
483  }
484  .demo-section[_ngcontent-ng-c312145568]   .preview[_ngcontent-ng-c312145568] {
485    margin-left: calc(-1 * var(--spacing-lg));
486    margin-right: calc(-1 * var(--spacing-lg));
487  }
488  .demo-section[_ngcontent-ng-c312145568]   .preview[_ngcontent-ng-c312145568]   video[_ngcontent-ng-c312145568] {
489    border-radius: 0;
490  }
491}</style><style>.stepper[_ngcontent-ng-c1779684384] {
492  padding: var(--spacing-5xl) 0;
493}
494
495.header[_ngcontent-ng-c1779684384] {
496  display: flex;
497  align-items: center;
498  justify-content: space-between;
499  margin-bottom: var(--spacing-xl);
500}
501
502.title[_ngcontent-ng-c1779684384] {
503  font-size: var(--font-size-3xl);
504  color: var(--on-surface);
505  margin-bottom: var(--spacing-md);
506}
507
508.subtitle[_ngcontent-ng-c1779684384] {
509  font-family: var(--font-inter);
510  font-size: var(--font-size-lg);
511  font-weight: 400;
512  line-height: var(--line-height-relaxed);
513  color: var(--on-surface-container);
514  text-wrap: pretty;
515}
516.subtitle[_ngcontent-ng-c1779684384]   strong[_ngcontent-ng-c1779684384] {
517  font-weight: 600;
518  color: var(--on-surface);
519}
520
521.nav[_ngcontent-ng-c1779684384] {
522  display: flex;
523  gap: var(--spacing-xs);
524}
525
526.nav-btn[_ngcontent-ng-c1779684384] {
527  display: flex;
528  align-items: center;
529  justify-content: center;
530  width: 48px;
531  height: 48px;
532  border: 1px solid var(--border-alt);
533  border-radius: 50%;
534  background: transparent;
535  cursor: pointer;
536  transition: opacity var(--transition-fast);
537}
538.nav-btn.disabled[_ngcontent-ng-c1779684384] {
539  opacity: 0.3;
540  cursor: default;
541}
542
543.nav-icon[_ngcontent-ng-c1779684384] {
544  display: block;
545  width: 11px;
546  height: 20px;
547  background-color: var(--on-surface);
548  mask-size: contain;
549  mask-repeat: no-repeat;
550  mask-position: center;
551}
552
553.track[_ngcontent-ng-c1779684384] {
554  --_inset: max(var(--spacing-lg), calc((100% - var(--container-max-width)) / 2 + var(--spacing-lg)));
555  display: flex;
556  gap: var(--spacing-md);
557  overflow-x: auto;
558  scroll-snap-type: x mandatory;
559  scroll-padding-left: var(--_inset);
560  scrollbar-width: none;
561  height: 437px;
562  cursor: grab;
563  user-select: none;
564  padding-left: var(--_inset);
565  padding-right: var(--_inset);
566}
567.track.grabbing[_ngcontent-ng-c1779684384] {
568  cursor: grabbing;
569  scroll-snap-type: none;
570}
571.track[_ngcontent-ng-c1779684384]::-webkit-scrollbar {
572  display: none;
573}
574
575.step[_ngcontent-ng-c1779684384] {
576  flex-shrink: 0;
577  width: 300px;
578  height: var(--step-height);
579  background-color: var(--step-bg);
580  color: var(--step-color);
581  border-radius: var(--border-radius-xs);
582  padding: var(--spacing-md);
583  display: flex;
584  flex-direction: column;
585  justify-content: space-between;
586  overflow: hidden;
587  scroll-snap-align: start;
588  transition: height 0.3s ease;
589  background-repeat: no-repeat;
590  background-position: top right;
591  background-size: auto var(--step-hover-height);
592  font-family: var(--font-inter);
593  line-height: 24px;
594}
595.step[_ngcontent-ng-c1779684384]:hover {
596  height: var(--step-hover-height);
597}
598.step[_ngcontent-ng-c1779684384]:hover   .step-subtitle[_ngcontent-ng-c1779684384] {
599  opacity: 0;
600  height: 0;
601  margin: 0;
602}
603.step[_ngcontent-ng-c1779684384]:hover   .step-description[_ngcontent-ng-c1779684384] {
604  opacity: 1;
605  height: auto;
606  margin-top: 8px;
607}
608
609.step-badge[_ngcontent-ng-c1779684384] {
610  width: 40px;
611  height: 40px;
612  background-color: var(--surface-container);
613  border-radius: var(--border-radius-sm);
614  display: flex;
615  align-items: center;
616  justify-content: center;
617  font-size: 24px;
618  font-weight: 400;
619  color: var(--on-surface);
620}
621
622.step-content[_ngcontent-ng-c1779684384] {
623  display: flex;
624  flex-direction: column;
625}
626
627.step-title[_ngcontent-ng-c1779684384] {
628  font-size: 16px;
629  font-weight: 500;
630  line-height: 24px;
631}
632
633.step-subtitle[_ngcontent-ng-c1779684384] {
634  font-size: 16px;
635  font-weight: 400;
636  opacity: 1;
637  line-height: 24px;
638  transition: opacity 0.3s ease, height 0.3s ease;
639}
640
641.step-description[_ngcontent-ng-c1779684384] {
642  font-family: var(--font-inter);
643  font-size: 16px;
644  font-weight: 400;
645  line-height: 24px;
646  opacity: 0;
647  height: 0;
648  overflow: hidden;
649  transition: opacity 0.3s ease, height 0.3s ease;
650}
651
652@media (max-width: 768px) {
653  .title[_ngcontent-ng-c1779684384] {
654    font-size: var(--font-size-3xl);
655  }
656  .subtitle[_ngcontent-ng-c1779684384] {
657    font-size: var(--font-size-base);
658  }
659  .step[_ngcontent-ng-c1779684384] {
660    width: 260px;
661    height: var(--step-hover-height);
662  }
663  .step[_ngcontent-ng-c1779684384]   .step-subtitle[_ngcontent-ng-c1779684384] {
664    opacity: 0;
665    height: 0;
666    margin: 0;
667  }
668  .step[_ngcontent-ng-c1779684384]   .step-description[_ngcontent-ng-c1779684384] {
669    opacity: 1;
670    height: auto;
671    margin-top: 8px;
672  }
673}</style><style>.focus-tabs[_ngcontent-ng-c2440753150] {
674  padding: var(--spacing-5xl) 0;
675  background-color: var(--surface-container);
676}
677.focus-tabs[_ngcontent-ng-c2440753150]   .title[_ngcontent-ng-c2440753150] {
678  font-size: var(--font-size-3xl);
679  margin-bottom: var(--spacing-2xl);
680}
681.focus-tabs[_ngcontent-ng-c2440753150]   .layout[_ngcontent-ng-c2440753150] {
682  display: grid;
683  grid-template-columns: 500fr 668fr;
684  gap: var(--spacing-lg);
685  align-items: start;
686}
687.focus-tabs[_ngcontent-ng-c2440753150]   .tabs[_ngcontent-ng-c2440753150] {
688  display: flex;
689  flex-direction: column;
690  gap: var(--spacing-sm);
691}
692.focus-tabs[_ngcontent-ng-c2440753150]   .tab[_ngcontent-ng-c2440753150] {
693  display: flex;
694  align-items: center;
695  gap: var(--spacing-md);
696  padding: var(--spacing-md) var(--spacing-lg);
697  border: 1px solid transparent;
698  border-radius: var(--border-radius-xs);
699  background: none;
700  cursor: pointer;
701  text-align: left;
702  font-family: var(--font-body);
703  transition: background-color var(--transition-fast), border-color var(--transition-fast);
704}
705.focus-tabs[_ngcontent-ng-c2440753150]   .tab[_ngcontent-ng-c2440753150]:hover {
706  background-color: var(--surface);
707}
708.focus-tabs[_ngcontent-ng-c2440753150]   .tab.active[_ngcontent-ng-c2440753150] {
709  background-color: var(--surface);
710  border-color: var(--border-alt);
711}
712.focus-tabs[_ngcontent-ng-c2440753150]   .tab.active[_ngcontent-ng-c2440753150]   .tab-icon[_ngcontent-ng-c2440753150] {
713  background-color: var(--primary);
714}
715.focus-tabs[_ngcontent-ng-c2440753150]   .tab.active[_ngcontent-ng-c2440753150]   .tab-title[_ngcontent-ng-c2440753150] {
716  color: var(--primary);
717}
718.focus-tabs[_ngcontent-ng-c2440753150]   .tab.active[_ngcontent-ng-c2440753150]   .tab-description[_ngcontent-ng-c2440753150] {
719  color: var(--on-surface);
720}
721.focus-tabs[_ngcontent-ng-c2440753150]   .tab-icon[_ngcontent-ng-c2440753150] {
722  flex-shrink: 0;
723  width: 24px;
724  height: 24px;
725  background-color: var(--on-surface);
726  mask-size: contain;
727  mask-repeat: no-repeat;
728  mask-position: center;
729  transition: background-color var(--transition-fast);
730}
731.focus-tabs[_ngcontent-ng-c2440753150]   .tab-title[_ngcontent-ng-c2440753150] {
732  display: block;
733  font-size: 16px;
734  font-weight: 500;
735  color: var(--on-surface);
736  transition: color var(--transition-fast);
737}
738.focus-tabs[_ngcontent-ng-c2440753150]   .tab-description[_ngcontent-ng-c2440753150] {
739  display: block;
740  font-size: 16px;
741  color: var(--on-surface-container);
742  margin-top: 2px;
743  font-family: var(--font-inter);
744  line-height: 24px;
745}
746.focus-tabs[_ngcontent-ng-c2440753150]   .btn-download[_ngcontent-ng-c2440753150] {
747  display: inline-flex;
748  align-items: center;
749  justify-content: center;
750  white-space: nowrap;
751  margin-top: var(--spacing-md);
752  padding: var(--spacing-sm) var(--spacing-lg);
753  border-radius: var(--border-radius-sm);
754  border: 4px solid var(--primary-border);
755  background-color: var(--primary);
756  color: var(--surface);
757  font-weight: 600;
758  font-size: var(--font-size-base);
759  transition: opacity var(--transition-fast);
760}
761.focus-tabs[_ngcontent-ng-c2440753150]   .btn-download[_ngcontent-ng-c2440753150]:hover {
762  opacity: 0.9;
763}
764.focus-tabs[_ngcontent-ng-c2440753150]   .panel[_ngcontent-ng-c2440753150] {
765  background-color: var(--tertiary);
766  border-radius: var(--border-radius-xs);
767  position: relative;
768  overflow: hidden;
769  align-self: stretch;
770}
771.focus-tabs[_ngcontent-ng-c2440753150]   .tab-panel[_ngcontent-ng-c2440753150] {
772  display: none;
773  background-color: var(--tertiary);
774  border-radius: var(--border-radius-xs);
775  position: relative;
776  overflow: hidden;
777  aspect-ratio: 668/580;
778}
779.focus-tabs[_ngcontent-ng-c2440753150]   .frame[_ngcontent-ng-c2440753150] {
780  position: absolute;
781  inset: 7% 0 0 5%;
782  border-radius: var(--border-radius-md) 0 0 0;
783  overflow: hidden;
784  box-shadow: -16px 16px 8px 0 rgba(0, 0, 0, 0.25);
785}
786.focus-tabs[_ngcontent-ng-c2440753150]   .panel-image[_ngcontent-ng-c2440753150], 
787.focus-tabs[_ngcontent-ng-c2440753150]   .tab-image[_ngcontent-ng-c2440753150] {
788  position: absolute;
789  top: 0;
790  left: 0;
791  display: block;
792  width: 100%;
793  height: 100%;
794  object-fit: cover;
795  object-position: left bottom;
796}
797@media (max-width: 768px) {
798  .focus-tabs[_ngcontent-ng-c2440753150]   .layout[_ngcontent-ng-c2440753150] {
799    grid-template-columns: 1fr;
800  }
801  .focus-tabs[_ngcontent-ng-c2440753150]   .tabs[_ngcontent-ng-c2440753150] {
802    gap: var(--spacing-2xl);
803  }
804  .focus-tabs[_ngcontent-ng-c2440753150]   .tab-image[_ngcontent-ng-c2440753150] {
805    object-position: left top;
806    transform: scale(1.9);
807    transform-origin: top left;
808  }
809  .focus-tabs[_ngcontent-ng-c2440753150]   .tab-block[_ngcontent-ng-c2440753150] {
810    display: flex;
811    flex-direction: column;
812    gap: var(--spacing-md);
813  }
814  .focus-tabs[_ngcontent-ng-c2440753150]   .tab-icon[_ngcontent-ng-c2440753150] {
815    display: none;
816  }
817  .focus-tabs[_ngcontent-ng-c2440753150]   .tab[_ngcontent-ng-c2440753150] {
818    padding: 0;
819    cursor: default;
820    pointer-events: none;
821  }
822  .focus-tabs[_ngcontent-ng-c2440753150]   .tab[_ngcontent-ng-c2440753150]:hover {
823    background-color: transparent;
824  }
825  .focus-tabs[_ngcontent-ng-c2440753150]   .tab.active[_ngcontent-ng-c2440753150] {
826    background-color: transparent;
827    border-color: transparent;
828  }
829  .focus-tabs[_ngcontent-ng-c2440753150]   .tab.active[_ngcontent-ng-c2440753150]   .tab-icon[_ngcontent-ng-c2440753150] {
830    background-color: var(--on-surface);
831  }
832  .focus-tabs[_ngcontent-ng-c2440753150]   .tab.active[_ngcontent-ng-c2440753150]   .tab-title[_ngcontent-ng-c2440753150] {
833    color: var(--on-surface);
834  }
835  .focus-tabs[_ngcontent-ng-c2440753150]   .tab-panel[_ngcontent-ng-c2440753150] {
836    display: block;
837  }
838  .focus-tabs[_ngcontent-ng-c2440753150]   .panel[_ngcontent-ng-c2440753150] {
839    display: none;
840  }
841}</style><style>.partners-preview[_ngcontent-ng-c3308003094] {
842  padding: var(--spacing-5xl) 0;
843}
844.partners-preview[_ngcontent-ng-c3308003094]   .header[_ngcontent-ng-c3308003094] {
845  display: flex;
846  justify-content: space-between;
847  align-items: center;
848  margin-bottom: var(--spacing-2xl);
849}
850.partners-preview[_ngcontent-ng-c3308003094]   .title[_ngcontent-ng-c3308003094] {
851  font-size: var(--font-size-3xl);
852}
853.partners-preview[_ngcontent-ng-c3308003094]   .link[_ngcontent-ng-c3308003094] {
854  display: inline-flex;
855  align-items: center;
856  gap: var(--spacing-xs);
857  font-size: var(--font-size-base);
858  font-weight: 600;
859  color: var(--on-surface);
860  transition: color var(--transition-fast);
861}
862.partners-preview[_ngcontent-ng-c3308003094]   .link[_ngcontent-ng-c3308003094]:hover {
863  color: var(--primary);
864}
865.partners-preview[_ngcontent-ng-c3308003094]   .link[_ngcontent-ng-c3308003094]:focus-visible {
866  outline: 2px solid var(--border-focus);
867  outline-offset: 2px;
868  border-radius: var(--border-radius-xs);
869}
870.partners-preview[_ngcontent-ng-c3308003094]   .icon-arrow[_ngcontent-ng-c3308003094] {
871  display: inline-block;
872  width: 16px;
873  height: 16px;
874  background-color: currentColor;
875  mask-image: url("/assets/icons/arrow-diagonal.svg");
876  mask-size: contain;
877  mask-repeat: no-repeat;
878}
879@media (max-width: 768px) {
880  .partners-preview[_ngcontent-ng-c3308003094]   .header[_ngcontent-ng-c3308003094] {
881    flex-direction: column;
882    align-items: flex-start;
883    gap: var(--spacing-sm);
884    margin-bottom: var(--spacing-xl);
885  }
886}</style><style>.blog-preview[_ngcontent-ng-c4283817964] {
887  padding: var(--spacing-5xl) 0;
888  background-color: var(--surface-container);
889}
890.blog-preview[_ngcontent-ng-c4283817964]   .header[_ngcontent-ng-c4283817964] {
891  display: flex;
892  justify-content: space-between;
893  align-items: center;
894  margin-bottom: var(--spacing-2xl);
895}
896.blog-preview[_ngcontent-ng-c4283817964]   .title[_ngcontent-ng-c4283817964] {
897  font-size: var(--font-size-3xl);
898}
899.blog-preview[_ngcontent-ng-c4283817964]   .link[_ngcontent-ng-c4283817964] {
900  display: inline-flex;
901  align-items: center;
902  gap: var(--spacing-xs);
903  font-size: var(--font-size-base);
904  font-weight: 600;
905  color: var(--on-surface);
906  transition: color var(--transition-fast);
907}
908.blog-preview[_ngcontent-ng-c4283817964]   .link[_ngcontent-ng-c4283817964]:hover {
909  color: var(--primary);
910}
911.blog-preview[_ngcontent-ng-c4283817964]   .icon-arrow[_ngcontent-ng-c4283817964] {
912  display: inline-block;
913  width: 16px;
914  height: 16px;
915  background-color: currentColor;
916  mask-image: url("/assets/icons/arrow-diagonal.svg");
917  mask-size: contain;
918  mask-repeat: no-repeat;
919}
920.blog-preview[_ngcontent-ng-c4283817964]   .grid[_ngcontent-ng-c4283817964] {
921  display: grid;
922  grid-template-columns: repeat(3, minmax(0, 1fr));
923  gap: var(--spacing-lg);
924}
925@media (min-width: 769px) {
926  .blog-preview[_ngcontent-ng-c4283817964]   .grid[_ngcontent-ng-c4283817964]    > pw-post-card[_ngcontent-ng-c4283817964]:nth-child(n+4) {
927    display: none;
928  }
929}
930@media (max-width: 768px) {
931  .blog-preview[_ngcontent-ng-c4283817964]   .link[_ngcontent-ng-c4283817964] {
932    display: none;
933  }
934  .blog-preview[_ngcontent-ng-c4283817964]   .grid[_ngcontent-ng-c4283817964] {
935    display: flex;
936    flex-direction: row;
937    gap: var(--spacing-md);
938    overflow-x: auto;
939    scroll-snap-type: x mandatory;
940    -webkit-overflow-scrolling: touch;
941    scrollbar-width: none;
942  }
943  .blog-preview[_ngcontent-ng-c4283817964]   .grid[_ngcontent-ng-c4283817964]::-webkit-scrollbar {
944    display: none;
945  }
946  .blog-preview[_ngcontent-ng-c4283817964]   .grid[_ngcontent-ng-c4283817964]   pw-post-card[_ngcontent-ng-c4283817964] {
947    flex: 0 0 80%;
948    max-width: 300px;
949    scroll-snap-align: start;
950  }
951}</style><style>.get-started[_ngcontent-ng-c4078681124]   .container[_ngcontent-ng-c4078681124] {
952  display: flex;
953  flex-direction: column;
954  align-items: center;
955  justify-content: center;
956  height: 400px;
957  gap: 48px;
958}
959.get-started[_ngcontent-ng-c4078681124]   .title[_ngcontent-ng-c4078681124] {
960  font-size: 48px;
961  font-weight: 500;
962  text-align: center;
963  color: var(--on-surface);
964}
965.get-started[_ngcontent-ng-c4078681124]   .btn[_ngcontent-ng-c4078681124] {
966  display: inline-flex;
967  align-items: center;
968  justify-content: center;
969  padding: var(--spacing-sm) var(--spacing-lg);
970  border-radius: var(--border-radius-sm);
971  border: 4px solid var(--primary-border);
972  background-color: var(--primary);
973  color: var(--const-white);
974  font-weight: 600;
975  font-size: var(--font-size-base);
976  text-decoration: none;
977  transition: opacity var(--transition-fast);
978}
979.get-started[_ngcontent-ng-c4078681124]   .btn[_ngcontent-ng-c4078681124]:hover {
980  opacity: 0.9;
981}
982@media (max-width: 768px) {
983  .get-started[_ngcontent-ng-c4078681124]   .title[_ngcontent-ng-c4078681124] {
984    font-size: var(--font-size-3xl);
985  }
986}</style><style>.footer[_ngcontent-ng-c1940239115] {
987  background-color: var(--const-black);
988  padding: var(--spacing-3xl) 0 var(--spacing-xl);
989}
990.footer[_ngcontent-ng-c1940239115]   .links[_ngcontent-ng-c1940239115] {
991  display: flex;
992  align-items: flex-start;
993  justify-content: center;
994  gap: var(--spacing-3xl);
995  margin-top: var(--spacing-2xl);
996}
997.footer[_ngcontent-ng-c1940239115]   .col[_ngcontent-ng-c1940239115] {
998  display: flex;
999  flex-direction: column;
1000  gap: var(--spacing-xs);
1001}
1002.footer[_ngcontent-ng-c1940239115]   .col-title[_ngcontent-ng-c1940239115] {
1003  font-size: var(--font-size-base);
1004  font-weight: 600;
1005  color: var(--const-white);
1006  margin-bottom: var(--spacing-xs);
1007}
1008.footer[_ngcontent-ng-c1940239115]   .link[_ngcontent-ng-c1940239115] {
1009  font-size: var(--font-size-nav);
1010  color: var(--const-grey);
1011  transition: color var(--transition-fast);
1012}
1013.footer[_ngcontent-ng-c1940239115]   .link[_ngcontent-ng-c1940239115]:hover {
1014  color: var(--const-white);
1015}
1016.footer[_ngcontent-ng-c1940239115]   .social-link[_ngcontent-ng-c1940239115] {
1017  display: flex;
1018  color: var(--const-grey);
1019  transition: color var(--transition-fast);
1020}
1021.footer[_ngcontent-ng-c1940239115]   .social-link[_ngcontent-ng-c1940239115]:hover {
1022  color: var(--const-white);
1023}
1024.footer[_ngcontent-ng-c1940239115]   .social-link[_ngcontent-ng-c1940239115]   .icon[_ngcontent-ng-c1940239115] {
1025  display: block;
1026  width: 20px;
1027  height: 20px;
1028  background-color: currentColor;
1029  mask-image: url("/assets/icons/linkedin.svg");
1030  mask-size: contain;
1031  mask-repeat: no-repeat;
1032  mask-position: center;
1033}
1034.footer[_ngcontent-ng-c1940239115]   .copyright[_ngcontent-ng-c1940239115] {
1035  margin-top: var(--spacing-2xl);
1036  text-align: center;
1037  font-size: 12px;
1038  font-weight: 300;
1039  color: var(--const-grey);
1040}
1041.footer[_ngcontent-ng-c1940239115]   .email[_ngcontent-ng-c1940239115] {
1042  font-weight: 400;
1043  color: var(--const-grey);
1044}
1045.footer[_ngcontent-ng-c1940239115]   .email[_ngcontent-ng-c1940239115]:hover {
1046  color: var(--const-white);
1047}
1048@media (max-width: 768px) {
1049  .footer[_ngcontent-ng-c1940239115]   .links[_ngcontent-ng-c1940239115] {
1050    flex-wrap: wrap;
1051    gap: var(--spacing-lg) var(--spacing-xl);
1052  }
1053  .footer[_ngcontent-ng-c1940239115]   .link[_ngcontent-ng-c1940239115] {
1054    white-space: nowrap;
1055  }
1056}</style><style>[_nghost-ng-c1482773486] {
1057      display: block;
1058      position: absolute;
1059      inset: 0;
1060      overflow: hidden;
1061    }
1062
1063    canvas[_ngcontent-ng-c1482773486] {
1064      width: 100%;
1065      height: 100%;
1066      background: var(--surface);
1067    }</style><style>.partners[_ngcontent-ng-c3721682887] {
1068  --cols: 5;
1069  display: grid;
1070  grid-template-columns: repeat(var(--cols), minmax(0, 1fr));
1071  gap: var(--spacing-xl);
1072}
1073.partners[_ngcontent-ng-c3721682887]   .logo[_ngcontent-ng-c3721682887] {
1074  position: relative;
1075  display: block;
1076  width: 100%;
1077  height: 56px;
1078  padding: 0;
1079  border: none;
1080  background: none;
1081  cursor: pointer;
1082  opacity: 0.55;
1083  transition: opacity var(--transition-fast);
1084}
1085.partners[_ngcontent-ng-c3721682887]   .logo[_ngcontent-ng-c3721682887]:hover, .partners[_ngcontent-ng-c3721682887]   .logo.active[_ngcontent-ng-c3721682887] {
1086  opacity: 1;
1087}
1088.partners[_ngcontent-ng-c3721682887]   .logo[_ngcontent-ng-c3721682887]:focus-visible {
1089  outline: 2px solid var(--border-focus);
1090  outline-offset: 6px;
1091  border-radius: var(--border-radius-xs);
1092}
1093.partners[_ngcontent-ng-c3721682887]   .logo[_ngcontent-ng-c3721682887]::after {
1094  content: "";
1095  position: absolute;
1096  left: 0;
1097  right: 0;
1098  bottom: calc(-1 * var(--spacing-md));
1099  height: 2px;
1100  background-color: var(--primary);
1101  scale: 0 1;
1102  transition: scale var(--transition-fast);
1103}
1104.partners[_ngcontent-ng-c3721682887]   .logo.active[_ngcontent-ng-c3721682887]::after {
1105  scale: 1 1;
1106}
1107.partners[_ngcontent-ng-c3721682887]   .logo[_ngcontent-ng-c3721682887]   img[_ngcontent-ng-c3721682887] {
1108  object-fit: contain;
1109  filter: grayscale(1) invert(1);
1110  mix-blend-mode: screen;
1111  transition: filter var(--transition-fast);
1112}
1113.partners[_ngcontent-ng-c3721682887]   .logo.active[_ngcontent-ng-c3721682887]   img[_ngcontent-ng-c3721682887], .partners.static[_ngcontent-ng-c3721682887]   .logo[_ngcontent-ng-c3721682887]   img[_ngcontent-ng-c3721682887] {
1114  filter: invert(1) hue-rotate(180deg);
1115}
1116.partners.static[_ngcontent-ng-c3721682887]   .logo[_ngcontent-ng-c3721682887] {
1117  opacity: 1;
1118  pointer-events: none;
1119}
1120.partners[_ngcontent-ng-c3721682887]   .panel[_ngcontent-ng-c3721682887] {
1121  grid-column: 1/-1;
1122  grid-row-start: calc(round(down, var(--i) / var(--cols), 1) + 2);
1123  display: grid;
1124  grid-template-columns: minmax(0, 1fr) minmax(0, 2fr);
1125  gap: var(--spacing-3xl);
1126  padding-top: var(--spacing-xl);
1127  border-top: 1px solid var(--border-alt);
1128  transition: opacity var(--transition-fast), translate var(--transition-fast);
1129}
1130@starting-style {
1131  .partners[_ngcontent-ng-c3721682887]   .panel[_ngcontent-ng-c3721682887] {
1132    opacity: 0;
1133    translate: 0 6px;
1134  }
1135}
1136.partners[_ngcontent-ng-c3721682887]   .panel[hidden][_ngcontent-ng-c3721682887] {
1137  display: none;
1138}
1139.partners[_ngcontent-ng-c3721682887]   .name[_ngcontent-ng-c3721682887] {
1140  font-size: var(--font-size-xl);
1141  font-weight: 600;
1142  line-height: 1.3;
1143  color: var(--on-surface);
1144}
1145.partners[_ngcontent-ng-c3721682887]   .tagline[_ngcontent-ng-c3721682887] {
1146  margin-top: var(--spacing-2xs);
1147  font-family: var(--font-inter);
1148  font-size: var(--font-size-base);
1149  line-height: var(--line-height-body);
1150  color: var(--on-surface-container);
1151  text-wrap: pretty;
1152}
1153.partners[_ngcontent-ng-c3721682887]   .description[_ngcontent-ng-c3721682887] {
1154  font-family: var(--font-inter);
1155  font-size: var(--font-size-md);
1156  line-height: var(--line-height-relaxed);
1157  color: var(--on-surface);
1158  text-wrap: pretty;
1159}
1160@media (prefers-color-scheme: light) {
1161  .partners[_ngcontent-ng-c3721682887]   .logo[_ngcontent-ng-c3721682887]   img[_ngcontent-ng-c3721682887] {
1162    filter: grayscale(1);
1163    mix-blend-mode: multiply;
1164  }
1165  .partners[_ngcontent-ng-c3721682887]   .logo.active[_ngcontent-ng-c3721682887]   img[_ngcontent-ng-c3721682887], .partners.static[_ngcontent-ng-c3721682887]   .logo[_ngcontent-ng-c3721682887]   img[_ngcontent-ng-c3721682887] {
1166    filter: none;
1167  }
1168}
1169@media (prefers-reduced-motion: reduce) {
1170  .partners[_ngcontent-ng-c3721682887]   .logo[_ngcontent-ng-c3721682887], 
1171   .partners[_ngcontent-ng-c3721682887]   .logo[_ngcontent-ng-c3721682887]::after, 
1172   .partners[_ngcontent-ng-c3721682887]   .logo[_ngcontent-ng-c3721682887]   img[_ngcontent-ng-c3721682887], 
1173   .partners[_ngcontent-ng-c3721682887]   .panel[_ngcontent-ng-c3721682887] {
1174    transition: none;
1175  }
1176}
1177@media (max-width: 640px) {
1178  .partners[_ngcontent-ng-c3721682887] {
1179    --cols: 2;
1180    gap: var(--spacing-xl) var(--spacing-lg);
1181  }
1182  .partners[_ngcontent-ng-c3721682887]   .logo[_ngcontent-ng-c3721682887] {
1183    height: 44px;
1184  }
1185  .partners[_ngcontent-ng-c3721682887]   .panel[_ngcontent-ng-c3721682887] {
1186    grid-template-columns: minmax(0, 1fr);
1187    gap: var(--spacing-md);
1188  }
1189}</style>
1189<script id="ld-jsonld-page" type="application/ld+json">{"@context":"https://schema.org","@type":"SoftwareApplication","@id":"https://prettywhale.ai/#product","name":"PrettyWhale.ai","applicationCategory":"DeveloperApplication","softwareRequirements":"Visual Studio Code","url":"https://prettywhale.ai/","description":"Generates data pipelines from a sample of data, with its unit tests, data quality checks, schemas, orchestration scripts and documentation, ready to deploy in production.","featureList":["Data pipelines generated from a data sample","Unit tests","Data quality checks","Schema generation","Orchestration and deployment scripts","Documentation"],"publisher":{"@id":"https://prettywhale.ai/#organization"}}</script>
1189<style>@charset "UTF-8";
1190[_nghost-ng-c2265933664] {
1191  display: block;
1192}
1193
1194.card[_ngcontent-ng-c2265933664] {
1195  display: flex;
1196  flex-direction: column;
1197  gap: var(--spacing-lg);
1198  height: 100%;
1199  padding: var(--spacing-lg);
1200  border-radius: var(--border-radius-xs);
1201  border: 1px solid var(--border-alt);
1202  background-color: var(--surface);
1203  overflow: hidden;
1204  transition: border-color var(--transition-fast);
1205}
1206
1207.badge[_ngcontent-ng-c2265933664] {
1208  display: inline-flex;
1209  align-items: center;
1210  align-self: flex-start;
1211  height: 24px;
1212  padding: 0 var(--spacing-xs);
1213  border: 1px solid transparent;
1214  border-radius: var(--border-radius-xs);
1215  font-family: var(--font-inter);
1216  font-weight: 500;
1217  font-size: var(--font-size-sm);
1218  line-height: 16px;
1219}
1220
1221
1222
1223.badge.neutral[_ngcontent-ng-c2265933664] {
1224  background-color: var(--surface-container);
1225  border-color: var(--border-alt);
1226  color: var(--on-surface);
1227}
1228
1229.body[_ngcontent-ng-c2265933664] {
1230  display: flex;
1231  flex-direction: column;
1232  gap: var(--spacing-xs);
1233}
1234
1235.card-title[_ngcontent-ng-c2265933664] {
1236  font-family: var(--font-heading);
1237  font-weight: 500;
1238  font-size: var(--font-size-base);
1239  line-height: 24px;
1240  color: var(--on-surface);
1241  overflow-wrap: break-word;
1242  transition: color var(--transition-fast);
1243  display: -webkit-box;
1244  -webkit-line-clamp: 2;
1245  line-clamp: 2;
1246  -webkit-box-orient: vertical;
1247  overflow: hidden;
1248}
1249
1250.card[_ngcontent-ng-c2265933664]:hover   .card-title[_ngcontent-ng-c2265933664] {
1251  color: var(--primary);
1252}
1253
1254.card-meta[_ngcontent-ng-c2265933664] {
1255  display: flex;
1256  align-items: center;
1257  gap: var(--spacing-xs);
1258  font-family: var(--font-inter);
1259  font-weight: 400;
1260  font-size: var(--font-size-base);
1261  line-height: 24px;
1262  color: var(--on-surface-disable);
1263}
1264
1265.excerpt[_ngcontent-ng-c2265933664] {
1266  font-family: var(--font-inter);
1267  font-weight: 400;
1268  font-size: var(--font-size-base);
1269  line-height: 24px;
1270  color: var(--on-surface-container);
1271  display: -webkit-box;
1272  -webkit-line-clamp: 3;
1273  -webkit-box-orient: vertical;
1274  overflow: hidden;
1275}
1276
1277
1278
1279.explore[_ngcontent-ng-c2265933664] {
1280  display: inline-flex;
1281  align-items: center;
1282  align-self: flex-start;
1283  gap: var(--spacing-xs);
1284  margin-top: auto;
1285  font-family: var(--font-inter);
1286  font-weight: 500;
1287  font-size: var(--font-size-sm);
1288  line-height: 16px;
1289  color: var(--on-surface);
1290  transition: color var(--transition-fast);
1291}
1292
1293.card[_ngcontent-ng-c2265933664]:hover   .explore[_ngcontent-ng-c2265933664] {
1294  color: var(--primary);
1295}
1296
1297.icon-arrow[_ngcontent-ng-c2265933664] {
1298  display: inline-block;
1299  width: 16px;
1300  height: 16px;
1301  background-color: currentColor;
1302  mask-image: url("/assets/icons/arrow-diagonal.svg");
1303  mask-size: contain;
1304  mask-repeat: no-repeat;
1305}
1306
1307
1308
1309.size-large[_nghost-ng-c2265933664]   .excerpt[_ngcontent-ng-c2265933664] {
1310  -webkit-line-clamp: 4;
1311}
1312
1313
1314
1315.size-small[_nghost-ng-c2265933664]   .card[_ngcontent-ng-c2265933664] {
1316  gap: var(--spacing-xs);
1317  justify-content: center;
1318}
1319.size-small[_nghost-ng-c2265933664]   .excerpt[_ngcontent-ng-c2265933664] {
1320  -webkit-line-clamp: 1;
1321}</style></head>
1322
1323<body><!--nghm-->
1324  <pw-root ng-version="21.2.5" ngh="8" ng-server-context="ssr-analog"><router-outlet></router-outlet><ng-component ngh="1"><pw-header _nghost-ng-c1321049901="" ngh="0"><header _ngcontent-ng-c1321049901="" class="header"><div _ngcontent-ng-c1321049901="" class="inner container"><a _ngcontent-ng-c1321049901="" routerlink="/" class="logo" href="/"><img _ngcontent-ng-c1321049901="" ngsrc="/assets/prettywhale_white.svg" alt="PrettyWhale logo" width="90" height="41" priority="" loading="eager" fetchpriority="high" decoding="sync" ng-img="true" src="/assets/prettywhale_white.svg"><span _ngcontent-ng-c1321049901="" class="logo-text"> PrettyWhale<span _ngcontent-ng-c1321049901="" class="logo-accent">.ai</span></span></a><nav _ngcontent-ng-c1321049901="" class="nav"><a _ngcontent-ng-c1321049901="" routerlink="/" class="link" href="/">Product</a><a _ngcontent-ng-c1321049901="" routerlink="/engineering-ai" class="link" href="/engineering-ai">Engineering AI</a><div _ngcontent-ng-c1321049901="" class="resources">
1324<button _ngcontent-ng-c1321049901="" type="button" aria-controls="resources-menu" class="link resources-toggle" aria-expanded="false"> Resources <span _ngcontent-ng-c1321049901="" aria-hidden="true" class="icon-chevron"></span></button><span _ngcontent-ng-c1321049901="" aria-hidden="true" class="resources-heading">Resources</span><ul _ngcontent-ng-c1321049901="" id="resources-menu" role="list" class="resources-menu"><li _ngcontent-ng-c1321049901=""><!----><a _ngcontent-ng-c1321049901="" class="link resources-link" href="/blog"> Blog </a><!----></li><li _ngcontent-ng-c1321049901=""><!----><a _ngcontent-ng-c1321049901="" class="link resources-link" href="/faq"> FAQ </a><!----></li><li _ngcontent-ng-c1321049901=""><!----><a _ngcontent-ng-c1321049901="" class="link resources-link" href="/career"> Careers </a><!----></li><li _ngcontent-ng-c1321049901=""><!----><a _ngcontent-ng-c1321049901="" class="link resources-link" href="/partners"> Partners </a><!----></li><li _ngcontent-ng-c1321049901=""><a _ngcontent-ng-c1321049901="" target="_blank" rel="noopener noreferrer" class="link resources-link" href="https://docs.prettywhale.ai/introduction">Docs</a><!----><!----></li><!----></ul></div><a _ngcontent-ng-c1321049901="" target="_blank" rel="noopener noreferrer" aria-label="Install for VS Code" class="install" href="https://marketplace.visualstudio.com/items?itemName=prettywhale-ai.prettywhale"><span _ngcontent-ng-c1321049901="" aria-hidden="true" class="icon-editor"></span> Install <span _ngcontent-ng-c1321049901="" aria-hidden="true" class="icon-arrow"></span></a><a _ngcontent-ng-c1321049901="" routerlink="/contact" class="contact" href="/contact">Ask for a demo</a></nav><button _ngcontent-ng-c1321049901="" aria-label="Toggle navigation menu" class="burger" aria-expanded="false"><span _ngcontent-ng-c1321049901="" class="burger-line"></span><span _ngcontent-ng-c1321049901="" class="burger-line"></span><span _ngcontent-ng-c1321049901="" class="burger-line"></span></button></div></header></pw-header><main><pw-hero _nghost-ng-c2863248315="" ngh="1"><section _ngcontent-ng-c2863248315="" class="hero"><pw-water-caustic _ngcontent-ng-c2863248315="" _nghost-ng-c1482773486="" ngh="1"><canvas _ngcontent-ng-c1482773486=""></canvas></pw-water-caustic><div _ngcontent-ng-c2863248315="" class="inner container"><div _ngcontent-ng-c2863248315="" class="content"><h1 _ngcontent-ng-c2863248315="" class="title"> Ingestion Code You <span _ngcontent-ng-c2863248315="" class="gradient">Trust</span></h1><p _ngcontent-ng-c2863248315="" class="subtitle"> Generate your data ingestion pipelines effortlessly.<br _ngcontent-ng-c2863248315=""></p><div _ngcontent-ng-c2863248315="" class="actions"><a _ngcontent-ng-c2863248315="" target="_blank" rel="noopener noreferrer" class="install-link" href="https://marketplace.visualstudio.com/items?itemName=prettywhale-ai.prettywhale"><span _ngcontent-ng-c2863248315="" aria-hidden="true" class="icon-editor"></span> Install for VS Code <span _ngcontent-ng-c2863248315="" aria-hidden="true" class="icon-arrow"></span></a><a _ngcontent-ng-c2863248315="" routerlink="/contact" class="contact-link" href="/contact">Contact us</a></div></div><div _ngcontent-ng-c2863248315="" class="visual"><img _ngcontent-ng-c2863248315="" ngsrc="assets/code.svg" alt="Code example" width="600" height="375" priority="" loading="eager" fetchpriority="high" decoding="sync" ng-img="true" src="assets/code.svg"></div></div></section></pw-hero><pw-demo-section _nghost-ng-c312145568="" ngh="1"><section _ngcontent-ng-c312145568="" class="demo-section"><div _ngcontent-ng-c312145568="" class="container"><div _ngcontent-ng-c312145568="" class="heading"><h2 _ngcontent-ng-c312145568="" class="title">We build an Engineering AI for data teams</h2><p _ngcontent-ng-c312145568="" class="subtitle"> PrettyWhale.ai is an Engineering AI built on a specialized small language model (SLM) trained for one job: <br _ngcontent-ng-c312145568=""><strong _ngcontent-ng-c312145568="">generate ingestion and integration code.</strong></p></div><div _ngcontent-ng-c312145568="" class="preview"><video _ngcontent-ng-c312145568="" src="assets/videos/prettywhale-teaser-dark.mp4" loop="" muted="" playsinline=""></video></div></div></section></pw-demo-section><pw-stepper _nghost-ng-c1779684384="" ngh="2"><section _ngcontent-ng-c1779684384="" class="stepper"><div _ngcontent-ng-c1779684384="" class="container"><div _ngcontent-ng-c1779684384="" class="header"><div _ngcontent-ng-c1779684384="" class="heading"><h2 _ngcontent-ng-c1779684384="" class="title">How it works</h2><p _ngcontent-ng-c1779684384="" class="subtitle"> From a sample of data to a complete pipeline, effortlessly. </p></div><div _ngcontent-ng-c1779684384="" class="nav"><button _ngcontent-ng-c1779684384="" aria-label="Scroll left" class="nav-btn disabled" disabled=""><span _ngcontent-ng-c1779684384="" aria-hidden="true" class="nav-icon" style="mask-image: url('/assets/icons/large-chevron-left.svg'); -webkit-mask-image: url('/assets/icons/large-chevron-left.svg');"></span></button><button _ngcontent-ng-c1779684384="" aria-label="Scroll right" class="nav-btn disabled" disabled=""><span _ngcontent-ng-c1779684384="" aria-hidden="true" class="nav-icon" style="mask-image: url('/assets/icons/large-chevron-right.svg'); -webkit-mask-image: url('/assets/icons/large-chevron-right.svg');"></span></button></div></div></div><div _ngcontent-ng-c1779684384="" class="track"><article _ngcontent-ng-c1779684384="" class="step" style="--step-bg: rgba(23, 40, 93, 1); --step-color: var(--const-white); --step-height: 333px; --step-hover-height: 361px; background-image: url(/assets/step-1.svg);"><div _ngcontent-ng-c1779684384="" class="step-badge">I</div><div _ngcontent-ng-c1779684384="" class="step-content"><h3 _ngcontent-ng-c1779684384="" class="step-title">Discovery</h3><p _ngcontent-ng-c1779684384="" class="step-subtitle">Identification sample analysis</p><p _ngcontent-ng-c1779684384="" class="step-description">Import a sample file (JSON, CSV, etc.) so PrettyWhale.ai can automatically analyze its structure, detect fields, and understand how your data is organized. No manual setup required.</p></div></article><article _ngcontent-ng-c1779684384="" class="step" style="--step-bg: var(--primary); --step-color: var(--const-black); --step-height: 275px; --step-hover-height: 327px; background-image: url(/assets/step-2.svg);"><div _ngcontent-ng-c1779684384="" class="step-badge">II</div><div _ngcontent-ng-c1779684384="" class="step-content"><h3 _ngcontent-ng-c1779684384="" class="step-title">Selection</h3><p _ngcontent-ng-c1779684384="" class="step-subtitle">Scope of data</p><p _ngcontent-ng-c1779684384="" class="step-description">PrettyWhale.ai generates a detailed analysis of your data, highlighting each field with examples and insights. You can then select only the fields you want to keep, giving you full control over your dataset.</p></div></article><article _ngcontent-ng-c1779684384="" class="step" style="--step-bg: var(--branding); --step-color: var(--const-black); --step-height: 304px; --step-hover-height: 395px; background-image: none;"><div _ngcontent-ng-c1779684384="" class="step-badge">III</div><div _ngcontent-ng-c1779684384="" class="step-content"><h3 _ngcontent-ng-c1779684384="" class="step-title">Processings</h3><p _ngcontent-ng-c1779684384="" class="step-subtitle">Transform suggestion</p><p _ngcontent-ng-c1779684384="" class="step-description">Based on the analysis, PrettyWhale.ai suggests relevant transformations such as normalization, formatting, and data cleaning. You can easily review, adjust, remove, or add your own transformations to match your exact requirements.</p></div></article><article _ngcontent-ng-c1779684384="" class="step" style="--step-bg: var(--tertiary); --step-color: var(--const-white); --step-height: 340px; --step-hover-height: 437px; background-image: url(/assets/step-4.svg);"><div _ngcontent-ng-c1779684384="" class="step-badge">IV</div><div _ngcontent-ng-c1779684384="" class="step-content"><h3 _ngcontent-ng-c1779684384="" class="step-title">Enrichments</h3><p _ngcontent-ng-c1779684384="" class="step-subtitle">Suggestion and selection</p><p _ngcontent-ng-c1779684384="" class="step-description">Enhance your dataset by connecting to external data sources. PrettyWhale.ai helps you enrich your data with additional context, making it more complete and valuable for downstream use.</p></div></article><article _ngcontent-ng-c1779684384="" class="step" style="--step-bg: var(--primary); --step-color: var(--const-black); --step-height: 333px; --step-hover-height: 361px; background-image: none;"><div _ngcontent-ng-c1779684384="" class="step-badge">V</div><div _ngcontent-ng-c1779684384="" class="step-content"><h3 _ngcontent-ng-c1779684384="" class="step-title">Output generation</h3><p _ngcontent-ng-c1779684384="" class="step-subtitle">Code and deliverables</p><p _ngcontent-ng-c1779684384="" class="step-description">PrettyWhale.ai generates everything you need: ingestion code, documentation, schemas, and unit tests. The code is validated and ready to be deployed in production, saving hours of manual work.</p></div></article><!----></div></section></pw-stepper><pw-focus-tabs _nghost-ng-c2440753150="" ngh="3"><section _ngcontent-ng-c2440753150="" class="focus-tabs"><div _ngcontent-ng-c2440753150="" class="container"><h2 _ngcontent-ng-c2440753150="" class="title">Focus on what matters</h2><div _ngcontent-ng-c2440753150="" class="layout"><div _ngcontent-ng-c2440753150="" class="tabs"><div _ngcontent-ng-c2440753150="" class="tab-block"><button _ngcontent-ng-c2440753150="" class="tab active"><span _ngcontent-ng-c2440753150="" aria-hidden="true" class="tab-icon" style="mask-image: url(/assets/icons/debug-step-over.svg); -webkit-mask-image: url(/assets/icons/debug-step-over.svg);"></span><div _ngcontent-ng-c2440753150=""><span _ngcontent-ng-c2440753150="" class="tab-title">Skip the repetitive work</span><span _ngcontent-ng-c2440753150="" class="tab-description">Stop wasting time manually writing your data pipelines. Generate your ingestion scripts in minutes!</span></div></button><div _ngcontent-ng-c2440753150="" class="tab-panel"><div _ngcontent-ng-c2440753150="" class="frame"><img _ngcontent-ng-c2440753150="" class="tab-image" src="/assets/focus-tab-1.png" alt="Skip the repetitive work"></div></div></div><div _ngcontent-ng-c2440753150="" class="tab-block"><button _ngcontent-ng-c2440753150="" class="tab"><span _ngcontent-ng-c2440753150="" aria-hidden="true" class="tab-icon" style="mask-image: url(/assets/icons/file-code.svg); -webkit-mask-image: url(/assets/icons/file-code.svg);"></span><div _ngcontent-ng-c2440753150=""><span _ngcontent-ng-c2440753150="" class="tab-title">Native data quality integration</span><span _ngcontent-ng-c2440753150="" class="tab-description">Writing validation code is time-consuming and tedious. PrettyWhale.ai takes care of it for you: it analyses your data sources and generates the necessary Data quality checks. </span></div></button><div _ngcontent-ng-c2440753150="" class="tab-panel"><div _ngcontent-ng-c2440753150="" class="frame"><img _ngcontent-ng-c2440753150="" class="tab-image" src="/assets/focus-tab-2.png" alt="Native data quality integration"></div></div></div><div _ngcontent-ng-c2440753150="" class="tab-block"><button _ngcontent-ng-c2440753150="" class="tab"><span _ngcontent-ng-c2440753150="" aria-hidden="true" class="tab-icon" style="mask-image: url(/assets/icons/copy.svg); -webkit-mask-image: url(/assets/icons/copy.svg);"></span><div _ngcontent-ng-c2440753150=""><span _ngcontent-ng-c2440753150="" class="tab-title">Standardize code for maintenance</span><span _ngcontent-ng-c2440753150="" class="tab-description">
1324AI generates structured, well-documented code that complies with industry standards, making debugging and code maintenance much easier.</span></div></button><div _ngcontent-ng-c2440753150="" class="tab-panel"><div _ngcontent-ng-c2440753150="" class="frame"><img _ngcontent-ng-c2440753150="" class="tab-image" src="/assets/focus-tab-3.png" alt="Standardize code for maintenance"></div></div></div><div _ngcontent-ng-c2440753150="" class="tab-block"><button _ngcontent-ng-c2440753150="" class="tab"><span _ngcontent-ng-c2440753150="" aria-hidden="true" class="tab-icon" style="mask-image: url(/assets/icons/verified.svg); -webkit-mask-image: url(/assets/icons/verified.svg);"></span><div _ngcontent-ng-c2440753150=""><span _ngcontent-ng-c2440753150="" class="tab-title">Production ready from day one</span><span _ngcontent-ng-c2440753150="" class="tab-description">Integrate unit tests, quality checks, and deployment and orchestration scripts directly into your generated code. Shorten the development cycle and deliver robust data flows free from beginner’s mistakes.</span></div></button><div _ngcontent-ng-c2440753150="" class="tab-panel"><div _ngcontent-ng-c2440753150="" class="frame"><img _ngcontent-ng-c2440753150="" class="tab-image" src="/assets/focus-tab-4.png" alt="Production ready from day one"></div></div></div><!----></div><div _ngcontent-ng-c2440753150="" class="panel"><div _ngcontent-ng-c2440753150="" class="frame"><img _ngcontent-ng-c2440753150="" class="panel-image" src="/assets/focus-tab-1.png" alt="Skip the repetitive work"></div></div></div></div></section></pw-focus-tabs><pw-partners-preview _nghost-ng-c3308003094="" ngh="1"><section _ngcontent-ng-c3308003094="" class="partners-preview"><div _ngcontent-ng-c3308003094="" class="container"><div _ngcontent-ng-c3308003094="" class="header"><h2 _ngcontent-ng-c3308003094="" class="title">Our partners</h2><a _ngcontent-ng-c3308003094="" routerlink="/partners" class="link" href="/partners"> Meet our partners <span _ngcontent-ng-c3308003094="" aria-hidden="true" class="icon-arrow"></span></a></div><pw-partner-list _ngcontent-ng-c3308003094="" _nghost-ng-c3721682887="" ngh="4"><div _ngcontent-ng-c3721682887="" role="group" aria-label="Partners" class="partners static"><!----><div _ngcontent-ng-c3721682887="" class="logo"><img _ngcontent-ng-c3721682887="" fill="" sizes="auto, (max-width: 640px) 40vw, 20vw" alt="Eleven" loading="lazy" fetchpriority="auto" decoding="auto" ng-img="true" src="/assets/partners/eleven.png" style="position: absolute; width: 100%; height: 100%; inset: 0;"></div><!----><!----><div _ngcontent-ng-c3721682887="" class="logo"><img _ngcontent-ng-c3721682887="" fill="" sizes="auto, (max-width: 640px) 40vw, 20vw" alt="Spoon Consulting" loading="lazy" fetchpriority="auto" decoding="auto" ng-img="true" src="/assets/partners/spoon-consulting.png" style="position: absolute; width: 100%; height: 100%; inset: 0;"></div><!----><!----><div _ngcontent-ng-c3721682887="" class="logo"><img _ngcontent-ng-c3721682887="" fill="" sizes="auto, (max-width: 640px) 40vw, 20vw" alt="Moove-SI" loading="lazy" fetchpriority="auto" decoding="auto" ng-img="true" src="/assets/partners/moove-si.png" style="position: absolute; width: 100%; height: 100%; inset: 0;"></div><!----><!----><div _ngcontent-ng-c3721682887="" class="logo"><img _ngcontent-ng-c3721682887="" fill="" sizes="auto, (max-width: 640px) 40vw, 20vw" alt="JEMS" loading="lazy" fetchpriority="auto" decoding="auto" ng-img="true" src="/assets/partners/jems.png" style="position: absolute; width: 100%; height: 100%; inset: 0;"></div><!----><!----><div _ngcontent-ng-c3721682887="" class="logo"><img _ngcontent-ng-c3721682887="" fill="" sizes="auto, (max-width: 640px) 40vw, 20vw" alt="D&amp;Ü" loading="lazy" fetchpriority="auto" decoding="auto" ng-img="true" src="/assets/partners/d-and-u.png" style="position: absolute; width: 100%; height: 100%; inset: 0;"></div><!----><!----><!----></div></pw-partner-list></div></section></pw-partners-preview><pw-blog-preview _nghost-ng-c4283817964="" ngh="6"><section _ngcontent-ng-c4283817964="" class="blog-preview"><div _ngcontent-ng-c4283817964="" class="container"><div _ngcontent-ng-c4283817964="" class="header"><h2 _ngcontent-ng-c4283817964="" class="title">PrettyWhale.ai blog</h2><a _ngcontent-ng-c4283817964="" routerlink="/blog" class="link" href="/blog"> Explore Articles <span _ngcontent-ng-c4283817964="" aria-hidden="true" class="icon-arrow"></span></a></div><div _ngcontent-ng-c4283817964="" class="grid"><pw-post-card _ngcontent-ng-c4283817964="" _nghost-ng-c2265933664="" class="" ngh="5"><a _ngcontent-ng-c2265933664="" class="card" href="/blog/the-great-ai-disruption-in-consulting"><span _ngcontent-ng-c2265933664="" class="badge" style="background-color: #FFC13C; border-color: #FFC13C; color: #121416;">Artificial Intelligence</span><!----><div _ngcontent-ng-c2265933664="" class="body"><h2 _ngcontent-ng-c2265933664="" class="card-title">The Great AI Disruption in Consulting</h2><p _ngcontent-ng-c2265933664="" class="card-meta"><span _ngcontent-ng-c2265933664="">6 min read</span><span _ngcontent-ng-c2265933664="" aria-hidden="true">•</span><!----><time _ngcontent-ng-c2265933664="" class="date">Sep 16, 2026</time></p><!----><p _ngcontent-ng-c2265933664="" class="excerpt">Discover how AI is dismantling the billable hour in IT services. Explore how the shift toward outcome-guaranteed contracts and asset-based consulting is redefining professional services.</p></div><!----></a></pw-post-card><pw-post-card _ngcontent-ng-c4283817964="" _nghost-ng-c2265933664="" class="" ngh="5"><a _ngcontent-ng-c2265933664="" class="card" href="/blog/the-age-of-generic-copilots-is-over-enter-specialized-harnesses"><span _ngcontent-ng-c2265933664="" class="badge" style="background-color: #FFC13C; border-color: #FFC13C; color: #121416;">Artificial Intelligence</span><!----><div _ngcontent-ng-c2265933664="" class="body"><h2 _ngcontent-ng-c2265933664="" class="card-title">The Age of Generic Copilots Is Over. Enter Specialized Harnesses.</h2><p _ngcontent-ng-c2265933664="" class="card-meta"><span _ngcontent-ng-c2265933664="">4 min read</span><span _ngcontent-ng-c2265933664="" aria-hidden="true">•</span><!----><time _ngcontent-ng-c2265933664="" class="date">Sep 3, 2026</time></p><!----><p _ngcontent-ng-c2265933664="" class="excerpt">Code generation became a commodity. What did not is the engineering frame around the model. Why the next wave is specialized harnesses, and what one contains for data.</p></div><!----></a></pw-post-card><pw-post-card _ngcontent-ng-c4283817964="" _nghost-ng-c2265933664="" class="" ngh="5"><a _ngcontent-ng-c2265933664="" class="card" href="/blog/data-engineering-doesnt-need-a-smarter-ai-copilot"><span _ngcontent-ng-c2265933664="" class="badge" style="background-color: #FF6B1A; border-color: #FF6B1A;
1324 color: #121416;">Data Engineering</span><!----><div _ngcontent-ng-c2265933664="" class="body"><h2 _ngcontent-ng-c2265933664="" class="card-title">Data Engineering Doesn't Need a Smarter AI Copilot</h2><p _ngcontent-ng-c2265933664="" class="card-meta"><span _ngcontent-ng-c2265933664="">5 min read</span><span _ngcontent-ng-c2265933664="" aria-hidden="true">•</span><!----><time _ngcontent-ng-c2265933664="" class="date">Sep 1, 2026</time></p><!----><p _ngcontent-ng-c2265933664="" class="excerpt">Every correction you type into a copilot is a requirement you already knew. Driving one means re-expressing your standards forever, one prompt at a time.</p></div><!----></a></pw-post-card><pw-post-card _ngcontent-ng-c4283817964="" _nghost-ng-c2265933664="" class="" ngh="5"><a _ngcontent-ng-c2265933664="" class="card" href="/blog/data-pipeline-vendor-lock-in-is-built-one-connector-at-a-time"><span _ngcontent-ng-c2265933664="" class="badge" style="background-color: #FF6B1A; border-color: #FF6B1A; color: #121416;">Data Engineering</span><!----><div _ngcontent-ng-c2265933664="" class="body"><h2 _ngcontent-ng-c2265933664="" class="card-title">Data Pipeline Vendor Lock-In Is Built One Connector at a Time</h2><p _ngcontent-ng-c2265933664="" class="card-meta"><span _ngcontent-ng-c2265933664="">5 min read</span><span _ngcontent-ng-c2265933664="" aria-hidden="true">•</span><!----><time _ngcontent-ng-c2265933664="" class="date">Aug 26, 2026</time></p><!----><p _ngcontent-ng-c2265933664="" class="excerpt">Lock-in is not a contract term, it is a rewrite estimate. Why the ingestion layer accumulates it faster than anything else, and how to measure yours today.</p></div><!----></a></pw-post-card><pw-post-card _ngcontent-ng-c4283817964="" _nghost-ng-c2265933664="" class="" ngh="5"><a _ngcontent-ng-c2265933664="" class="card" href="/blog/small-language-models-for-code-generation-when-narrow-beats-large"><span _ngcontent-ng-c2265933664="" class="badge" style="background-color: #FFC13C; border-color: #FFC13C; color: #121416;">Artificial Intelligence</span><!----><div _ngcontent-ng-c2265933664="" class="body"><h2 _ngcontent-ng-c2265933664="" class="card-title">Small Language Models for Code Generation: When Narrow Beats Large</h2><p _ngcontent-ng-c2265933664="" class="card-meta"><span _ngcontent-ng-c2265933664="">6 min read</span><span _ngcontent-ng-c2265933664="" aria-hidden="true">•</span><!----><time _ngcontent-ng-c2265933664="" class="date">Aug 20, 2026</time></p><!----><p _ngcontent-ng-c2265933664="" class="excerpt">Small language models can match large ones on a bounded job like code generation, at 10 to 30 times lower serving cost. Here is when that trade holds.</p></div><!----></a></pw-post-card><pw-post-card _ngcontent-ng-c4283817964="" _nghost-ng-c2265933664="" class="" ngh="5"><a _ngcontent-ng-c2265933664="" class="card" href="/blog/who-accepts-code-nobody-wrote"><span _ngcontent-ng-c2265933664="" class="badge" style="background-color: #FFC13C; border-color: #FFC13C; color: #121416;">Artificial Intelligence</span><!----><div _ngcontent-ng-c2265933664="" class="body"><h2 _ngcontent-ng-c2265933664="" class="card-title">Who Accepts AI-Generated Code That Nobody Wrote</h2><p _ngcontent-ng-c2265933664="" class="card-meta"><span _ngcontent-ng-c2265933664="">4 min read</span><span _ngcontent-ng-c2265933664="" aria-hidden="true">•</span><!----><time _ngcontent-ng-c2265933664="" class="date">Aug 17, 2026</time></p><!----><p _ngcontent-ng-c2265933664="" class="excerpt">Code review assumes an author who can answer for the code. Generated code breaks that assumption. What changes in the pull request, and after an incident.</p></div><!----></a></pw-post-card><pw-post-card _ngcontent-ng-c4283817964="" _nghost-ng-c2265933664="" class="" ngh="5"><a _ngcontent-ng-c2265933664="" class="card" href="/blog/what-production-ready-means-for-a-data-pipeline-and-who-decides"><span _ngcontent-ng-c2265933664="" class="badge" style="background-color: #FF6B1A; border-color: #FF6B1A; color: #121416;">Data Engineering</span><!----><div _ngcontent-ng-c2265933664="" class="body"><h2 _ngcontent-ng-c2265933664="" class="card-title">Production-Ready Data Pipelines: What It Means and Who Decides</h2><p _ngcontent-ng-c2265933664="" class="card-meta"><span _ngcontent-ng-c2265933664="">5 min read</span><span _ngcontent-ng-c2265933664="" aria-hidden="true">•</span><!----><time _ngcontent-ng-c2265933664="" class="date">Aug 12, 2026</time></p><!----><p _ngcontent-ng-c2265933664="" class="excerpt">Ask two engineers when an ingestion pipeline is finished and you get two answers. Here is how to write the bar down on one page and enforce it in review.</p></div><!----></a></pw-post-card><pw-post-card _ngcontent-ng-c4283817964="" _nghost-ng-c2265933664="" class="" ngh="5"><a _ngcontent-ng-c2265933664="" class="card" href="/blog/estimating-data-ingestion-work-a-unit-of-work-method"><span _ngcontent-ng-c2265933664="" class="badge" style="background-color: #FF6B1A; border-color: #FF6B1A;
1324 color: #121416;">Data Engineering</span><!----><div _ngcontent-ng-c2265933664="" class="body"><h2 _ngcontent-ng-c2265933664="" class="card-title">Estimating Data Ingestion Work: A Unit-of-Work Method</h2><p _ngcontent-ng-c2265933664="" class="card-meta"><span _ngcontent-ng-c2265933664="">7 min read</span><span _ngcontent-ng-c2265933664="" aria-hidden="true">•</span><!----><time _ngcontent-ng-c2265933664="" class="date">Aug 10, 2026</time></p><!----><p _ngcontent-ng-c2265933664="" class="excerpt">Stop estimating ingestion tasks one by one. Estimate units of work, build a tier grid from your own delivery history, and recalibrate it after every project.</p></div><!----></a></pw-post-card><pw-post-card _ngcontent-ng-c4283817964="" _nghost-ng-c2265933664="" class="" ngh="5"><a _ngcontent-ng-c2265933664="" class="card" href="/blog/moove-si-and-prettywhale-ai-team-up"><span _ngcontent-ng-c2265933664="" class="badge" style="background-color: #17285D; border-color: #17285D; color: #fafafa;">Partners</span><!----><div _ngcontent-ng-c2265933664="" class="body"><h2 _ngcontent-ng-c2265933664="" class="card-title">Moove-SI and PrettyWhale.ai Team Up on Data &amp; AI</h2><p _ngcontent-ng-c2265933664="" class="card-meta"><span _ngcontent-ng-c2265933664="">2 min read</span><span _ngcontent-ng-c2265933664="" aria-hidden="true">•</span><!----><time _ngcontent-ng-c2265933664="" class="date">Aug 6, 2026</time></p><!----><p _ngcontent-ng-c2265933664="" class="excerpt">Moove-SI, an integrator specialising in Microsoft solutions, is adding PrettyWhale.ai to its Data &amp; AI offer to speed up the costliest part of data projects.</p></div><!----></a></pw-post-card><pw-post-card _ngcontent-ng-c4283817964="" _nghost-ng-c2265933664="" class="" ngh="5"><a _ngcontent-ng-c2265933664="" class="card" href="/blog/engineering-ai-copilots-and-code-generators-a-taxonomy"><span _ngcontent-ng-c2265933664="" class="badge" style="background-color: #FFC13C; border-color: #FFC13C; color: #121416;">Artificial Intelligence</span><!----><div _ngcontent-ng-c2265933664="" class="body"><h2 _ngcontent-ng-c2265933664="" class="card-title">Engineering AI, Copilots and Code Generators: A Taxonomy</h2><p _ngcontent-ng-c2265933664="" class="card-meta"><span _ngcontent-ng-c2265933664="">6 min read</span><span _ngcontent-ng-c2265933664="" aria-hidden="true">•</span><!----><time _ngcontent-ng-c2265933664="" class="date">Aug 5, 2026</time></p><!----><p _ngcontent-ng-c2265933664="" class="excerpt">"AI writes code now" covers four product families with four failure modes. The criterion that separates them, and four questions to pick the right one.</p></div><!----></a></pw-post-card><pw-post-card _ngcontent-ng-c4283817964="" _nghost-ng-c2265933664="" class="" ngh="5"><a _ngcontent-ng-c2265933664="" class="card" href="/blog/the-end-of-the-it-services-company-model-based-solely-on-daily-rates"><span _ngcontent-ng-c2265933664="" class="badge" style="background-color: #FF6B1A; border-color: #FF6B1A; color: #121416;">Data Engineering</span><!----><div _ngcontent-ng-c2265933664="" class="body"><h2 _ngcontent-ng-c2265933664="" class="card-title">The End of the Daily-Rate Model for IT Services</h2><p _ngcontent-ng-c2265933664="" class="card-meta"><span _ngcontent-ng-c2265933664="">2 min read</span><span _ngcontent-ng-c2265933664="" aria-hidden="true">•</span><!----><time _ngcontent-ng-c2265933664="" class="date">Jun 17, 2026</time></p><!----><p _ngcontent-ng-c2265933664="" class="excerpt">For thirty years IT services firms sold human time by the day. Clients now ask what you save them, not how many consultants you can put on the project.</p></div><!----></a></pw-post-card><pw-post-card _ngcontent-ng-c4283817964="" _nghost-ng-c2265933664="" class="" ngh="5"><a _ngcontent-ng-c2265933664="" class="card" href="/blog/ingestion-vs-integration-code"><span _ngcontent-ng-c2265933664="" class="badge" style="background-color: #FF6B1A; border-color: #FF6B1A; color: #121416;">Data Engineering</span><!----><div _ngcontent-ng-c2265933664="" class="body"><h2 _ngcontent-ng-c2265933664="" class="card-title">Ingestion vs Integration: A Confusion That Costs Millions</h2><p _ngcontent-ng-c2265933664="" class="card-meta"><span _ngcontent-ng-c2265933664="">2 min read</span><span _ngcontent-ng-c2265933664="" aria-hidden="true">•</span><!----><time _ngcontent-ng-c2265933664="" class="date">Jun 12, 2026</time></p><!----><p _ngcontent-ng-c2265933664="" class="excerpt">Ingestion and integration code get used as synonyms. They solve different problems, and the teams that optimise the wrong one pay for it in production.</p></div><!----></a></pw-post-card><!----></div></div></section></pw-blog-preview><pw-get-started _nghost-ng-c4078681124="" data-url="https://prettywhale.notion.site/Getting-started-334fb7946c3d80949cbbc49cc275f670" ngh="1"><section _ngcontent-ng-c4078681124="" class="get-started"><div _ngcontent-ng-c4078681124="" class="container"><h2 _ngcontent-ng-c4078681124="" class="title">Start building production-ready pipelines today</h2></div></section></pw-get-started></main><pw-footer _nghost-ng-c1940239115="" ngh="7"><footer _ngcontent-ng-c1940239115="" class="footer"><div _ngcontent-ng-c1940239115="" class="container"><nav _ngcontent-ng-c1940239115="" class="links"><div _ngcontent-ng-c1940239115="" class="col"><h4 _ngcontent-ng-c1940239115="" class="col-title">Company</h4><a _ngcontent-ng-c1940239115="" routerlink="/engineering-ai" class="link" href="/engineering-ai">Engineering AI</a><a _ngcontent-ng-c1940239115="" routerlink="/contact" class="link" href="/contact">Contact</a></div><div _ngcontent-ng-c1940239115="" class="col"><h4 _ngcontent-ng-c1940239115="" class="col-title">Resources</h4><!----><a _ngcontent-ng-c1940239115="" class="link" href="/blog">Blog</a><!----><!----><a _ngcontent-ng-c1940239115="" class="link" href="/faq">FAQ</a><!----><!----><a _ngcontent-ng-c1940239115="" class="link" href="/career">Careers</a><!----><!----><a _ngcontent-ng-c1940239115="" class="link" href="/partners">Partners</a><!----><a _ngcontent-ng-c1940239115="" target="_blank" rel="noopener noreferrer" class="link" href="https://docs.prettywhale.ai/introduction"> Docs </a><!----><!----><!----></div><div _ngcontent-ng-c1940239115="" class="col"><h4 _ngcontent-ng-c1940239115="" class="col-title">Legal</h4><a _ngcontent-ng-c1940239115="" routerlink="/legal-notice" class="link" href="/legal-notice">Legal notice</a></div>
1324<a _ngcontent-ng-c1940239115="" href="https://www.linkedin.com/company/prettywhale-ai/" target="_blank" rel="noopener noreferrer" aria-label="PrettyWhale on LinkedIn" class="social-link"><span _ngcontent-ng-c1940239115="" class="icon linkedin"></span></a></nav><p _ngcontent-ng-c1940239115="" class="copyright"> © 2026 Copyright PrettyWhale.ai - Question about data privacy ? Send an email to <a _ngcontent-ng-c1940239115="" href="mailto:[email protected]" class="email">[email protected]</a></p></div></footer></pw-footer></ng-component><!----></pw-root>
1325
1326  <!-- LinkedIn Insight Tag -->
1327  
1327<script type="text/javascript">
1328    if (window.location.hostname === "prettywhale.ai") {
1329      _linkedin_partner_id = "10836025";
1330      window._linkedin_data_partner_ids = window._linkedin_data_partner_ids || [];
1331      window._linkedin_data_partner_ids.push(_linkedin_partner_id);
1332      (function (l) {
1333        if (!l) {
1334          window.lintrk = function (a, b) {
1335            window.lintrk.q.push([a, b]);
1336          };
1337          window.lintrk.q = [];
1338        }
1339        var s = document.getElementsByTagName("script")[0];
1340        var b = document.createElement("script");
1341        b.type = "text/javascript";
1342        b.async = true;
1343        b.src = "https://snap.licdn.com/li.lms-analytics/insight.min.js";
1344        s.parentNode.insertBefore(b, s);
1345      })(window.lintrk);
1346    }
1347  </script>
1347
1348  <noscript>
1349    <img height="1" width="1" style="display: none" alt="" src="https://px.ads.linkedin.com/collect/?pid=10836025&amp;fmt=gif">
1350  </noscript>
1351  
1352  
1353
1354
1355<script id="ng-state" type="application/json">{"analog_444896916":{"body":[{"title":"The Great AI Disruption in Consulting","slug":"the-great-ai-disruption-in-consulting","excerpt":"Discover how AI is dismantling the billable hour in IT services. Explore how the shift toward outcome-guaranteed contracts and asset-based consulting is redefining professional services.","html":"\u003Ch2 id=\"the-collapse-of-the-legacy-consulting-equation\">The Collapse of the Legacy Consulting Equation\u003C/h2>\u003Cp>For decades, the global professional services industry, management consultancies, and IT service providers built their financial engines around a single commercial equation: \u003Cstrong>Revenue = Billable Hours × Rate Cards\u003C/strong>. In this input-driven framework, firm profitability scaled proportionately with human headcount and engagement duration. The economic incentives were simple: the more consultants deployed and the longer a project took, the higher the top-line revenue.\u003C/p>\u003Cp>The rapid maturity of artificial intelligence (specifically generative algorithms, agentic coding tools, and automated workflow orchestrators) has fundamentally shattered this equation. By automating research, data synthesis, software development, and process modeling, AI compresses tasks that previously required weeks of human labor into minutes or hours.\u003C/p>\u003Cp>This acceleration forces professional services into a critical juncture. If a firm continues to bill clients on a Time and Materials (T&amp;M), using AI to increase delivery speed directly destroys its own top-line revenue. To avoid this structural trap, the consulting sector is undergoing its most radical transformation in modern history.\u003C/p>\u003Cp>By synthesizing macroeconomic data, strategic research, and M&amp;A trends, this state-of-the-market report explores how market leaders are abandoning legacy rate cards, establishing outcome-guaranteed commercial models, and acquiring software assets to decouple revenue growth from headcount expansion.\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Ch2 id=\"1-the-economic-collapse-of-input-based-consulting\">1. The Economic Collapse of Input-Based Consulting\u003C/h2>\u003Cp>The immediate reflex of most enterprise leaders when adopting a breakthrough technology is to seek task-level efficiency—performing existing work faster, cheaper, and with fewer resources. However, applying AI on top of legacy processes creates a severe market dynamic known as the \u003Cstrong>\"productivity trap\"\u003C/strong>.\u003C/p>\u003Cp>When general-purpose AI capabilities become widely accessible across the market, basic efficiency gains cease to function as a competitive differentiator. As competing service providers adopt the same automated tools, delivery speed is commoditized, and the resulting financial savings are rapidly passed downstream to clients or competed away entirely.\u003C/p>\u003Cp>This commoditization creates an immediate operational liability for input-based business models. Under traditional T&amp;M contracts, the client absorbs the financial risk of inefficiency while the provider is financially rewarded for extended project timelines. AI completely inverts this dynamic:\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>Procurement Backlash:\u003C/strong> Enterprise buyers and CFOs are acutely aware that consulting teams utilize AI code assistants, automated research tools, and prompt frameworks. Procurement teams increasingly reject standard hourly billing for work generated in seconds by algorithms.\u003Cbr>\u003C/li>\u003Cli>\u003Cstrong>Revenue Compression:\u003C/strong> When a 40-hour research or modeling deliverable is completed in 4 hours using AI, a consultancy billing on time and materials loses 90% of its contract value despite delivering a faster, higher-quality result.\u003Cbr>\u003C/li>\u003Cli>\u003Cstrong>Misaligned Execution Risk:\u003C/strong> In an AI-augmented environment, continuing to charge by the hour incentivizes firms to delay automation adoption internally to protect billing volume, creating friction between provider incentives and client expectations.\u003Cbr>\u003C/li>\u003C/ul>\u003Cp>As a result, time-and-materials billing is transitioning from a industry standard into a structural disadvantage. Sustainable profitability no longer comes from selling human labor by the hour, but from productizing intellectual property, redesigning end-to-e
1355nd workflows, and capturing the value created by continuous learning operating models.\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Ch2 id=\"2-re-architecting-contracts-from-hours-billed-to-outcome-guarantees\">2. Re-Architecting Contracts: From Hours Billed to Outcome Guarantees\u003C/h2>\u003Cp>To escape the productivity trap, forward-thinking service providers are overhauling their commercial terms. Instead of selling team capacity or analyst hours, consultancies are transitioning toward \u003Cstrong>Outcome-Based Consulting\u003C/strong> and value-based pricing models.\u003C/p>\u003Cp>In an outcome-guaranteed framework, contract value is tied directly to verifiable business metrics rather than activity inputs. Rather than billing $250,000 for 1,000 hours of supply chain analysis, a firm contracts for a guaranteed 15% reduction in inventory holding costs or a specific improvement in order-fulfillment cycles.\u003C/p>\n\u003C!--kg-card-begin: html-->\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>\u003Cstrong>Commercial Dimension\u003C/strong>\u003C/th>\n\u003Cth>\u003Cstrong>Legacy Input-Based Model (T&amp;M)\u003C/strong>\u003C/th>\n\u003Cth>\u003Cstrong>Modern Value-Based Model (Outcome-Guaranteed)\u003C/strong>\u003C/th>\n\u003C/tr>\n\u003C/thead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Primary Pricing Metric\u003C/strong>\u003C/td>\n\u003Ctd>Hours, days, and staff rates.\u003C/td>\n\u003Ctd>Business milestones, value-share, usage tiers.\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Financial Risk\u003C/strong>\u003C/td>\n\u003Ctd>Carried almost entirely by the client.\u003C/td>\n\u003Ctd>Shared between client and service provider.\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Provider Incentive\u003C/strong>\u003C/td>\n\u003Ctd>Maximize hours worked per engagement.\u003C/td>\n\u003Ctd>Deploy AI tools to achieve outcomes as fast as possible.\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Delivery Mechanism\u003C/strong>\u003C/td>\n\u003Ctd>Bespoke human labor per engagement.\u003C/td>\n\u003Ctd>Productized AI workflows, domain data, and IP assets.\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Scalability\u003C/strong>\u003C/td>\n\u003Ctd>Tied directly to employee headcount growth.\u003C/td>\n\u003Ctd>Decoupled from headcount via software platforms.\u003C/td>\n\u003C/tr>\n\u003C/tbody>\n\u003C/table>\n\u003C!--kg-card-end: html-->\n\u003Cp>This contractual shift fundamentally aligns the goals of the client and the provider. Under an outcome guarantee, the service provider is fully incentivized to deploy proprietary AI models, pre-built workflow automations, and agentic tools as aggressively as possible. The faster and more efficiently the provider achieves the contracted result, the higher its internal profit margin.\u003C/p>\u003Cp>Furthermore, this shift changes how enterprise IT programs measure success. Instead of tracking vanity metrics like \"hours eliminated\" or \"documents processed per analyst,\" leadership teams evaluate providers based on end-to-end friction reduction, cost-to-serve optimizations, and cycle-time compressions.\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Ch2 id=\"3-the-asset-arbitrage-scaling-revenue-without-scaling-headcount\">3. The Asset Arbitrage: Scaling Revenue Without Scaling Headcount\u003C/h2>\u003Cp>Transitioning to outcome-guaranteed contracts requires service providers to possess reusable intellectual property, automated platforms, and pre-trained industry models. Because building these assets organically takes time, global IT service giants are pursuing aggressive M&amp;A strategies to buy their way into software-led models.\u003C/p>\u003Cp>Market research tracking M&amp;A activity across 24 leading global service providers reveals a massive structural pivot toward \u003Cstrong>Asset-Based Consulting (ABC)\u003C/strong>:\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>Product-Driven Acquisitions:\u003C/strong> Out of 97 major acquisitions analyzed across top service firms, nearly \u003Cstrong>1 in 5 deals (20%)\u003C/strong> involved software products, digital platforms, or hybrid service-product solutions. Two-thirds of these technology deals were executed specifically to acquire the underlying software asset rather than talent or agency capacity.\u003Cbr>\u003C/li>\u003Cli>\u003Cstrong>The $9 Billion Non-FTE Mandate:\u003C/strong> Market leaders like Accenture have allocated up to \u003Cstrong>$9 billion\u003C/strong> in acquisition capital specifically to expand into \u003Cstrong>non-FTE (Full-Time Equivalent) commercial models\u003C/strong>. The strategic goal is to increase top-line market share without requiring a matching surge in employee headcount.\u003Cbr>\u003C/li>\u003Cli>\u003Cstrong>Hybrid Monetization Streams:\u003C/strong> By acquiring proprietary software platforms (ranging from cybersecurity orchestrators to industry-specific data engines), service providers can blend advisory fees with recurring SaaS subscriptions, platform usage rates, and performance bonuses.\u003Cbr>\u003C/li>\u003C/ul>\u003Cp>This M&amp;A wave represents an industry evolution from labor arbitrage to \u003Cstrong>asset and IP arbitrage\u003C/strong>. Instead of starting from scratch on every client engagement, modern consultancies arrive with pre-configured AI software assets that perform 70% of the heavy lifting out of the box. Human consultants then focus on high-value strategic positioning, organizational change management, and system integration.\u003C/p>\u003Cp>&nbsp;\u003C/p>
1355\n\u003Ch2 id=\"4-strategic-playbook-for-enterprise-buyers-and-service-leaders\">4. Strategic Playbook for Enterprise Buyers and Service Leaders\u003C/h2>\u003Cp>The convergence of outcome-based pricing, software asset acquisitions, and AI-driven automation requires a revised operating playbook for both corporate IT buyers and service provider executives.\u003C/p>\u003Ch3 id=\"for-enterprise-buyers-cios-and-procurement-teams\">For Enterprise Buyers, CIOs, and Procurement Teams\u003C/h3>\u003Col>\u003Cli>\u003Cstrong>Eliminate Blended Rate Cards for AI Projects:\u003C/strong> Stop signing open-ended Time &amp; Materials contracts for AI transformation projects. Require suppliers to structure Statements of Work (SOWs) around fixed business outcomes, transaction-based pricing, or shared-risk gainshare models.\u003C/li>\u003Cli>\u003Cstrong>Contract for Reusable IP Deliverables:\u003C/strong> When a service provider utilizes custom prompt libraries, analytical playbooks, or automated workflows during an engagement, explicitly define these artifacts as client-owned deliverables to enable long-term internal reuse.\u003C/li>\u003Cli>\u003Cstrong>Audit for Vendor Lock-In:\u003C/strong> As major service providers acquire independent software platforms, audit vendor choices during annual reviews to ensure that acquired software tools remain open, interoperable, and aligned with your broader enterprise architecture.\u003C/li>\u003C/ol>\u003Cp>&nbsp;\u003C/p>\n\u003Ch3 id=\"for-consultancies-and-service-executives\">For Consultancies, and Service Executives\u003C/h3>\u003Col>\u003Cli>\u003Cstrong>Productize Internal Frameworks:\u003C/strong> Audit internal delivery practices and convert bespoke methodologies, templates, and domain expertise into packaged, software-like assets and automated AI workflows.\u003C/li>\u003Cli>\u003Cstrong>Build Non-FTE Revenue Streams:\u003C/strong> Complement human advisory services with asset-backed recurring revenue, such as managed platform fees, continuous AI model maintenance, or subscription-based analytics.\u003C/li>\u003Cli>\u003Cstrong>Establish Clear Outcome Benchmarks:\u003C/strong> Develop precise, auditable baseline metrics (e.g., claims processing time, code deployment velocity, customer acquisition costs) so that value-based and outcome-guaranteed pricing contracts can be executed without contractual dispute.\u003C/li>\u003C/ol>\u003Cp>&nbsp;\u003C/p>\n\u003Ch2 id=\"conclusion-the-era-of-software-led-services\">Conclusion: The Era of Software-Led Services\u003C/h2>\u003Cp>The professional services and IT consulting industries are experiencing an irreversible structural shift. The era when firm growth depended purely on recruiting armies of junior analysts to sell billable hours is coming to a close.\u003C/p>\u003Cp>As market data demonstrates, attempting to rely on basic task efficiency traps service providers in margin compression. Meanwhile, enterprise buyers are rejecting input-based rate cards in favor of outcome-guaranteed engagements, and service giants are spending billions to acquire software assets that decouple revenue from headcount.\u003C/p>\u003Cp>In this new landscape, the most competitive IT service providers and consultancies will not be those with the largest headcount, but those that successfully combine proprietary software assets, outcome-based accountability, and AI-powered learning operating models to deliver guaranteed business value at unprecedented speed.\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Ch3 id=\"sources-references\">Sources &amp; References\u003C/h3>\u003Cul>\u003Cli>\u003Cstrong>McKinsey &amp; Company:\u003C/strong> \u003Cem>The Real AI Advantage\u003C/em> (McKinsey Global Institute Research &amp; Podcast featuring Tanguy Catlin, Roberta Fusaro, and Lucia Rahilly).\u003C/li>\u003Cli>\u003Cstrong>Cognitute:\u003C/strong> \u003Cem>Outcome-Based Consulting: Why Time and Materials Models Are Obsolete\u003C/em> (Cognitute Insights Framework).\u003C/li>\u003Cli>\u003Cstrong>Forrester Research:\u003C/strong> \u003Cem>Service Providers Are Acquiring Their Way To Relevance And Survival In An AI-Powered World\u003C/em>
1355 (Ted Schadler, VP and Principal Analyst).\u003C/li>\u003Cli>\u003Cstrong>MarketScale Business Services:\u003C/strong> \u003Cem>AI Is Pushing Consulting Away From Billable Hours and Toward Productized Learning Operating Models\u003C/em>.\u003C/li>\u003C/ul>\u003Cp>&nbsp;\u003C/p>\n\u003Chr>\u003Cdiv class=\"kg-card kg-cta-card kg-cta-bg-white kg-cta-immersive    kg-cta-centered\" data-layout=\"immersive\">\n            \n            \u003Cdiv class=\"kg-cta-content\">\n                \n                \n                    \u003Cdiv class=\"kg-cta-content-inner\">\n                    \n                        \u003Cdiv class=\"kg-cta-text\">\n                            \u003Cp>\u003Cspan style=\"white-space: pre-wrap;\">You have any questions on PrettyWhale.ai ? \u003C/span>\u003C/p>\n                        \u003C/div>\n                    \n                    \n                        \u003Ca href=\"https://prettywhale.ai/contact\" class=\"kg-cta-button \" style=\"background-color: #FF6B1A; color: #FFFFFF;\">\n                            Contact us\n                        \u003C/a>\n                        \n                    \u003C/div>\n                \n            \u003C/div>\n        \u003C/div>","published_at":"2026-09-16T10:50:32.000+00:00","reading_time":6,"feature_image":null,"primary_author":{"id":"69f1af43e6e1d40001b214b8","name":"Kevin BIESSY","slug":"kevin","profile_image":null,"cover_image":null,"bio":null,"website":null,"location":null,"facebook":null,"twitter":null,"threads":null,"bluesky":null,"mastodon":null,"tiktok":null,"youtube":null,"instagram":null,"linkedin":null,"meta_title":null,"meta_description":null,"url":"https://prettywhale.ai/author/kevin/"},"tags":[{"id":"6a4d1f89399f6a0001a8c99b","name":"Artificial Intelligence","slug":"artificial-intelligence","description":null,"feature_image":null,"visibility":"public","og_image":null,"og_title":null,"og_description":null,"twitter_image":null,"twitter_title":null,"twitter_description":null,"meta_title":null,"meta_description":null,"codeinjection_head":null,"codeinjection_foot":null,"canonical_url":null,"accent_color":"#FFC13C","url":"https://prettywhale.ai/tag/artificial-intelligence/"}]},{"title":"The Age of Generic Copilots Is Over. Enter Specialized Harnesses.","slug":"the-age-of-generic-copilots-is-over-enter-specialized-harnesses","excerpt":"Code generation became a commodity. What did not is the engineering frame around the model. Why the next wave is specialized harnesses, and what one contains for data.","html":"\u003Cp>For the past three years, the software industry has been obsessed with a single interface: the chat box. We were told that a large enough model, paired with a generic autocomplete plugin, would transform software engineering forever.\u003C/p>\u003Cp>It worked-up to a point: generating syntax, scaffolding boilerplates, or writing basic utility functions became trivial. But as the hype settles, engineering teams are facing a harsh reality: code generation is a solved problem; code verification and maintainability are the new issues.\u003C/p>\u003Cp>In specialized domains like Data Engineering, generic LLM copilots have hit a hard ceiling. The issue isn't the size of the context window or the reasoning capabilities of the underlying model. The issue is that generic tools lack an engineering frame.\u003C/p>\u003Cp>It is time to move past generic AI assistants. The next phase of engineering software belongs to specialized execution harnesses.\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Ch2 id=\"from-models-to-frameworks-an-industry-pattern\">From Models to Frameworks: An Industry Pattern\u003C/h2>\u003Cp>To understand where AI-assisted engineering is going, we only need to look at the history of software development.\u003C/p>\u003Cp>In the early days of web development, engineers wrote raw scripts directly against web servers. It was flexible, fast to deploy, and completely unmanageable at scale. Security vulnerabilities were rampant, folder structures were arbitrary, and maintaining someone else’s code was a nightmare.\u003C/p>\u003Cp>The industry didn't solve this by telling developers to \"think harder\" or write better comments. It solved 
1355it through frameworks. These systems didn't take away the developer's agency; they imposed strict conventions, automated testing setups, defined migration pathways, and standardized execution environments.\u003C/p>\u003Cp>Generative AI is undergoing the exact same transition. Prompting a raw LLM through a chat sidebar is the modern equivalent of writing unconstrained web scripts in 2002. It gives you instant code, but leaves you with all the architectural debt.\u003C/p>\u003Cp>Data Engineering doesn't just need a model that knows better Python syntax. It needs an execution framework that understands the physics of data.\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Ch2 id=\"why-generic-copilots-fail-at-data-integration\">Why Generic Copilots Fail at Data Integration\u003C/h2>\u003Cp>Web interfaces fail noisily with a broken component or a 500 status code. Data pipelines fail quietly.\u003C/p>\u003Cp>When an ingestion pipeline breaks due to a hidden API payload change, a missing null check, or an implicit type conversion, the pipeline rarely crashes immediately. Instead, it pollutes downstream data warehouses, corrupts analytical dashboards, and quietly wrecks machine learning models. You only discover the issue weeks later when financial reports don't reconcile.\u003C/p>\u003Cp>A generic AI copilot cannot prevent this because it operates without operational context:\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>It guesses instead of profiling:\u003C/strong> A copilot looks at a small snippet of JSON and assumes the schema. It doesn't analyze 100,000 rows to detect edge-case nullability, varying array lengths, or timestamp variations.\u003C/li>\u003Cli>\u003Cstrong>It lacks deterministic guardrails:\u003C/strong> Prompting an LLM to \"handle all errors and follow best practices\" is a suggestion, not a guarantee. The model will still omit retry logic or drop malformed records when context limits pressure it.\u003C/li>\u003Cli>\u003Cstrong>It shifts the burden of proof:\u003C/strong> When a copilot generates 200 lines of complex extraction code, the data engineer must manually audit every single line. The time saved writing code is lost reviewing hallucinated logic.\u003C/li>\u003C/ul>\u003Cp>Asking a generic LLM to write data pipelines without strict environmental constraints is simply asking for technical debt.\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Ch2 id=\"what-is-a-specialized-execution-harness\">What Is a Specialized Execution Harness?\u003C/h2>\u003Cp>A \u003Cstrong>Specialized Execution Harness\u003C/strong> is not a wrapper around an API, nor is it a complex system prompt. It is a deterministic execution environment built around a domain-specific model. It controls, constrains, validates, and tests every output produced by the AI before a human ever sees it.\u003C/p>\u003Cp>At \u003Ca href=\"https://prettywhale.ai/\" rel=\"noopener nofollow\">PrettyWhale.ai\u003C/a>, we designed our harness around the specific constraints of data ingestion and integration. Instead of relying on a multi-purpose LLM, the architecture couples a Small Language Model (SLM), trained explicitly on data engineering patterns, with a multi-stage validation engine.\u003C/p>\u003Cfigure class=\"kg-card kg-image-card\">\u003Cimg src=\"https://prettywhale.ai/content/images/2026/09/Pipeline-Flow-Diagram-selection-1.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"1280\" height=\"1068\" srcset=\"https://prettywhale.ai/content/images/size/w600/2026/09/Pipeline-Flow-Diagram-selection-1.png 600w, https://prettywhale.ai/content/images/size/w1000/2026/09/Pipeline-Flow-Diagram-selection-1.png 1000w, https://prettywhale.ai/content/images/2026/09/Pipeline-Flow-Diagram-selection-1.png 1280w\" sizes=\"(min-width: 720px) 720px\">\u003C/figure>\u003Cp>&nbsp;\u003C/p>\n\u003Ch2 id=\"the-4-pillars-of-a-data-engineering-harness\">The 4 Pillars of a Data Engineering Harness\u003C/h2>\u003Col>\u003Cli>\u003Cstrong>Deterministic Analysis First:\u003C/strong> Before any code is generated, the harness runs static and dynamic analysis on raw source payloads (JSON, CSV, Parquet, or API endpoints). It calculates schema shapes, field cardinality, and boundary conditions programmatically.\u003C/li>\u003Cli>\u003Cstrong>Context Enforcement:\u003C/strong> The harness forces the generation process to adhere strictly to target destination specifications, corporate retry policies, logging standards, and schema validation rules.\u003C/li>\u003Cli>\u003Cstrong>Automated Sandbox Execution:\u003C/strong> The harness does not present raw code to the engineer. It spins up an isolated sandbox, executes the generated pipeline against real data samples, and verifies that data lands correctly without silent loss.\u003C/li>\u003Cli>\u003Cstrong>Self-Correction Loop:\u003C/strong> If the sandbox execution fails or detects a schema mismatch, the execution trace is fed directly back to the model. The system fixes its own code inside the harness before delivering the final result.\u003C/li>\u003C/ol>\u003Cp>&nbsp;\u003C/p>\n\u003Ch2 id=\"from-code-generation-to-software-engineering-ai\">From \"Code Generation\" to \"Software Engineering AI\"\u003C/h2>\u003Cp>
1355The fundamental shift happening right now is from \u003Cstrong>unassisted code generation\u003C/strong> to \u003Cstrong>verified software assets\u003C/strong>.\u003C/p>\n\u003C!--kg-card-begin: html-->\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>\u003Cstrong>Generic Copilot Approach\u003C/strong>\u003C/th>\n\u003Cth>\u003Cstrong>Specialized Harness Approach\u003C/strong>\u003C/th>\n\u003C/tr>\n\u003C/thead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Input:\u003C/strong> Natural language prompt (\u003Cem>\"Write a connector for X\"\u003C/em>).\u003C/td>\n\u003Ctd>\u003Cstrong>Input:\u003C/strong> Actual data source sample and schema constraints.\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Process:\u003C/strong> Probabilistic completion based on web training data.\u003C/td>\n\u003Ctd>\u003Cstrong>Process:\u003C/strong> Profiling \u003Cspan class=\"math math-inline\">\\rightarrow\u003C/span> Specialized Generation \u003Cspan class=\"math math-inline\">\\rightarrow\u003C/span> Sandbox Execution.\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Output:\u003C/strong> Raw text file containing code.\u003C/td>\n\u003Ctd>\u003Cstrong>Output:\u003C/strong> Verified package (Code, passing tests, schema, docs).\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Review Method:\u003C/strong> Manual line-by-line code audit by an engineer.\u003C/td>\n\u003Ctd>\u003Cstrong>Review Method:\u003C/strong> Verification of execution proofs and test results.\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Failure Mode:\u003C/strong> Silent runtime failures in production.\u003C/td>\n\u003Ctd>\u003Cstrong>Failure Mode:\u003C/strong> Sandbox flags errors before deployment.\u003C/td>\n\u003C/tr>\n\u003C/tbody>\n\u003C/table>\n\u003C!--kg-card-end: html-->\n\u003Cp>When an AI system delivers code alongside its own execution proof, the developer's role evolves. You no longer spend 45 minutes line-debugging synthetic code. You spend two minutes reviewing verified test runs and architectural alignment.\u003C/p>\u003Cp>This is what turning AI into an engineering tool actually looks like.\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Ch2 id=\"the-future-belongs-to-domain-specificity\">The Future Belongs to Domain Specificity\u003C/h2>\u003Cp>The era of expecting a single chat model to design user interfaces, optimize SQL queries, configure Kubernetes clusters, and write data ingestion pipelines with equal reliability is over.\u003C/p>\u003Cp>Just as general-purpose CPUs gave rise to specialized GPUs for graphics and AI workloads, general-purpose LLMs are giving way to specialized harnesses for domain-critical engineering tasks.\u003C/p>\u003Cp>At PrettyWhale.ai, we built our execution harness because we believe data engineers shouldn't spend their lives writing or debugging fragile boilerplate ingestion scripts. By wrapping domain-trained models in a deterministic, self-verifying harness, we can finally give data teams what they actually need: pipelines that work, backed by proof, every single time.\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Chr>\u003Cp>\u003Cem>PrettyWhale.ai is an Engineering AI for ingestion and integration code: a specialised harness around a small model trained on that code alone. It works from a real sample of the source and hands back a deliverable that has already run on that sample and passed its tests.\u003C/em>\u003C/p>\u003Cdiv class=\"kg-card kg-cta-card kg-cta-bg-white kg-cta-immersive    kg-cta-centered\" data-layout=\"immersive\">\n            \n            \u003Cdiv class=\"kg-cta-content\">\n                \n                \n                    \u003Cdiv class=\"kg-cta-content-inner\">\n                    \n                        \u003Cdiv class=\"kg-cta-text\">\n                            \u003Cp>\u003Cspan style=\"white-space: pre-wrap;\">You have any questions on PrettyWhale.ai ? \u003C/span>\u003C/p>\n                        \u003C/div>\n                    \n                    \n                        \u003Ca href=\"https://prettywhale.ai/contact\" class=\"kg-cta-button \" style=\"background-color: #FF6B1A;
1355 color: #FFFFFF;\">\n                            Contact us\n                        \u003C/a>\n                        \n                    \u003C/div>\n                \n            \u003C/div>\n        \u003C/div>","published_at":"2026-09-03T08:39:32.000+00:00","reading_time":4,"feature_image":null,"primary_author":{"id":"69f1af43e6e1d40001b214b8","name":"Kevin BIESSY","slug":"kevin","profile_image":null,"cover_image":null,"bio":null,"website":null,"location":null,"facebook":null,"twitter":null,"threads":null,"bluesky":null,"mastodon":null,"tiktok":null,"youtube":null,"instagram":null,"linkedin":null,"meta_title":null,"meta_description":null,"url":"https://prettywhale.ai/author/kevin/"},"tags":[{"id":"6a4d1f89399f6a0001a8c99b","name":"Artificial Intelligence","slug":"artificial-intelligence","description":null,"feature_image":null,"visibility":"public","og_image":null,"og_title":null,"og_description":null,"twitter_image":null,"twitter_title":null,"twitter_description":null,"meta_title":null,"meta_description":null,"codeinjection_head":null,"codeinjection_foot":null,"canonical_url":null,"accent_color":"#FFC13C","url":"https://prettywhale.ai/tag/artificial-intelligence/"}]},{"title":"Data Engineering Doesn't Need a Smarter AI Copilot","slug":"data-engineering-doesnt-need-a-smarter-ai-copilot","excerpt":"Every correction you type into a copilot is a requirement you already knew. Driving one means re-expressing your standards forever, one prompt at a time.","html":"\u003Cp>Ask an AI copilot for a CSV reader and you get one in four seconds. It is competent. Then you start typing.\u003C/p>\u003Cul>\u003Cli>Pin the encoding, do not rely on the locale.\u003C/li>\u003Cli>The malformed input test should assert the behaviour, not just that it did not crash.\u003C/li>\u003Cli>Externalise the file path and the thresholds.\u003C/li>\u003Cli>Keep the original value when the conversion fails.\u003C/li>\u003Cli>Add a schema, and validate against it in CI.\u003C/li>\u003Cli>Failures per record, not one status for the run.\u003C/li>\u003C/ul>\u003Cp>Six corrections, all reasonable but all you knew before you asked. A few hours later, you have a good pipeline.\u003C/p>\u003Cp>Tomorrow there is another source, and you start again.\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Ch2 id=\"the-specification-exists-in-your-chat-history\">The specification exists in your chat history\u003C/h2>\u003Cp>That list is not feedback, it is your definition of done, expressed one prompt at a time, in a conversation that gets thrown away when the tab closes.\u003C/p>\u003Cp>This is the part that goes unnoticed, because each individual correction is cheap. A few hours is nothing. But you pay it per source, per project, and per person, and the sixth engineer on your team pays it without knowing the other five already did, because there is nowhere for that knowledge to live.\u003C/p>\u003Cp>Driving a copilot well means becoming very good at re-expressing your own standards. That is a real skill and it is a strange thing to get good at, because the value it produces evaporates every time.\u003C/p>\u003Cp>Here is a cheap measurement. Over your next week, keep a note of every instruction you give a generation tool that you have given it before. Not the ones about this specific source, the ones about how your team builds things. Then read the list.\u003C/p>\u003Cp>That list is a specification. It is not written down anywhere, and you are paying rent on it.\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Ch2 id=\"a-more-capable-model-does-not-fix-this\">A more capable model does not fix this\u003C/h2>\u003Cp>The intuitive answer is that this is a transitional problem. Models get better, first drafts get closer, corrections shrink toward zero.\u003C/p>\u003Cp>Half of that is true. Capability reduces the corrections about correctness: the logic errors, the misunderstood requirement, the function that does not compile. Those genuinely go away as models improve, and they have been going away.\u003C/p>\u003Cp>It does nothing for the other half, and the other half is most of your list.\u003C/p>\u003Cp>Pinning the encoding is not a matter of intelligence. Neither is asserting behaviour in the malformed case, or externalising configuration, or preserving the original value. These are conventions. They are decisions your team made, or should have made, and they are not deducible from the request because they are not properties of the world. They are properties of you.\u003C/p>\u003Cp>A model cannot infer your bar. It can only be told, and if the telling happens in a conversation, it has to happen again in the next conversation. No amount of capability closes that loop, because the loop is not about capability. It is about where the specification lives.\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Ch2 id=\"you-are-part-of-the-harness\">You are part of the harness\u003C/h2>\u003Cp>There is a shift worth borrowing in how people talk about these tools. The interesting part is no longer the model, it is the harness around it: the loop that calls tools, manages context, retries what failed, and decides when the work is done. Two products built on the same weights behave completely differently, and the harness is the difference.\u003C/p>\u003Cp>That framing is right, and it is worth pushing one step further. Ask what a general-purpose harness is able to verify.\u003C/p>\u003Cp>It can check the things that are true of all code. It compiles. The tests it wrote pass. The linter agrees. The types check. That is a genuinely useful set, and it is also the complete set, because a harness built to produce anything cannot assume anything about what finished looks like.\u003C/p>\u003Cp>Your conventions are not in that set. Which means a general harness has a hole exactly where the definition of done should be, and something has to fill it.\u003C/p>\u003Cp>You fill it. Every correction you type is the specification component of a harness that has no other source for it, and no memory of it once the conversation ends. That is not a complaint about copilots, it is 
1355what a general harness is: it borrows its standards from whoever happens to be driving.\u003C/p>\u003Cp>An Engineering AI is the same architecture with that hole closed. The domain is fixed, so the harness knows which artifacts a finished pipeline has and can check for them without being asked. The model inside is replaceable and getting cheaper every quarter. The harness is the product.\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Ch2 id=\"the-other-shape\">The other shape\u003C/h2>\u003Cp>Concretely, when a system generates a class of artifact from only one domain, the conventions stop being instructions and become part of what the thing is. The encoding is pinned because that is what a pipeline is. The tests cover the malformed case because a deliverable without that is not finished. There is nothing to ask for, because asking is only necessary when the tool could reasonably do otherwise.\u003C/p>\u003Cp>That constraint is the whole mechanism, and it is why the honest name for this is not \"better AI\". It is narrowness. A tool that produces one artifact can be held to a definition of done for that artifact. A tool that produces anything cannot be held to a definition of anything, which is exactly why a copilot hands you a starting point and leaves the remaining work with you.\u003C/p>\u003Cp>The work does not disappear either way, it moves. With a copilot, your work is directing. With a specialised generator, your work is deciding whether the result is acceptable, which is a smaller job and a more interesting one.\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Ch2 id=\"when-the-treadmill-is-the-right-thing-to-be-on\">When the treadmill is the right thing to be on\u003C/h2>\u003Cp>None of this is an argument against copilots, and the case for them is not weak.\u003C/p>\u003Cp>Work that is different every time has no conventions to encode. Exploratory work, where the point is to find out what you are building, cannot be generated against a definition of done because the definition arrives last. A one-off script has no second instance to amortise anything over. In all of those, a general tool driven by a person who knows what they want is the correct arrangement, and building a generator for it would be an expensive mistake.\u003C/p>\u003Cp>The treadmill is only a problem when the work repeats. And it is worth being precise about the test, because \"repetitive\" is used loosely.\u003C/p>\u003Cp>The work repeats when a new instance shares the same skeleton and differs only in details, when you can write down what a finished instance contains, and when the ways instances differ from each other form a list you could finish. Ingestion passes all three, which is why generating it works. Business logic fails the first, which is why it does not.\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Ch2 id=\"the-question-that-separates-them\">The question that separates them\u003C/h2>\u003Cp>Not which tool is smarter. This one:\u003C/p>\u003Cp>Am I giving instructions I have given before?\u003C/p>\u003Cp>If the answer is no, you are exploring, and a copilot is the right tool. If the answer is yes, and it is yes for most of the code most teams write, then you have a specification you have never written down, and you are re-typing it for the rest of your career.\u003C/p>\u003Cp>The future everyone describes is AI that writes more code. The one that matters is AI y
1355ou stop having to instruct.\u003C/p>\u003Cp>\u003C/p>\u003Chr>\u003Cp>\u003Cem>PrettyWhale.ai is an Engineering AI that generates ingestion and integration code. The conventions are not prompts, they are what the output is: executable code, tests that assert behaviour, quality checks per record, an output schema, configuration and documentation.\u003C/em>\u003C/p>\u003Cdiv class=\"kg-card kg-cta-card kg-cta-bg-white kg-cta-immersive    kg-cta-centered\" data-layout=\"immersive\">\n            \n            \u003Cdiv class=\"kg-cta-content\">\n                \n                \n                    \u003Cdiv class=\"kg-cta-content-inner\">\n                    \n                        \u003Cdiv class=\"kg-cta-text\">\n                            \u003Cp>\u003Cspan style=\"white-space: pre-wrap;\">You have any questions on PrettyWhale.ai ? \u003C/span>\u003C/p>\n                        \u003C/div>\n                    \n                    \n                        \u003Ca href=\"https://prettywhale.ai/contact\" class=\"kg-cta-button \" style=\"background-color: #FF6B1A; color: #FFFFFF;\">\n                            Contact us\n                        \u003C/a>\n                        \n                    \u003C/div>\n                \n            \u003C/div>\n        \u003C/div>","published_at":"2026-09-01T08:43:20.000+00:00","reading_time":5,"feature_image":null,"primary_author":{"id":"69f1af43e6e1d40001b214b8","name":"Kevin BIESSY","slug":"kevin","profile_image":null,"cover_image":null,"bio":null,"website":null,"location":null,"facebook":null,"twitter":null,"threads":null,"bluesky":null,"mastodon":null,"tiktok":null,"youtube":null,"instagram":null,"linkedin":null,"meta_title":null,"meta_description":null,"url":"https://prettywhale.ai/author/kevin/"},"tags":[{"id":"6a4d1ef0399f6a0001a8c991","name":"Data Engineering","slug":"data-engineering","description":null,"feature_image":null,"visibility":"public","og_image":null,"og_title":null,"og_description":null,"twitter_image":null,"twitter_title":null,"twitter_description":null,"meta_title":null,"meta_description":null,"codeinjection_head":null,"codeinjection_foot":null,"canonical_url":null,"accent_color":"#FF6B1A","url":"https://prettywhale.ai/tag/data-engineering/"}]},{"title":"Data Pipeline Vendor Lock-In Is Built One Connector at a Time","slug":"data-pipeline-vendor-lock-in-is-built-one-connector-at-a-time","excerpt":"Lock-in is not a contract term, it is a rewrite estimate. Why the ingestion layer accumulates it faster than anything else, and how to measure yours today.","html":"\u003Cp>Most conversations about vendor lock-in are about contracts. Term length, exit clauses, egress fees, proprietary formats. Those are the visible part, and they are the part a procurement team can negotiate.\u003C/p>\u003Cp>The part nobody negotiates is the one that decides whether leaving is possible at all. If you gave notice on Monday, what would you have to rebuild before you could run again?\u003C/p>\u003Cp>That number is your lock-in. Everything else is a line item.\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Ch2 id=\"1-lock-in-is-a-rewrite-estimate-not-a-contract-term\">1. Lock-in is a rewrite estimate, not a contract term\u003C/h2>\u003Cp>The reason the contract framing misleads is that the expensive thing is never the data. Data moves. Storage is a solved problem, formats are increasingly open, and a bulk export is a weekend at worst.\u003C/p>\u003Cp>What does not move is the logic that turned a source into something usable. The connector that knows this API paginates by page number. The rule that a null customer reference means the record predates the migration and should be routed, not dropped. The knowledge that this vendor sends dates in two formats depending on which of their systems generated the row.\u003C/p>\u003Cp>None of that is data. All of it is code, or worse, configuration inside somebody's product. And the cost of leaving is the cost of writing it again, by peo
1355ple who no longer remember why it was written the first time.\u003C/p>\u003Cp>So the useful question is not what the exit costs in euros. It is how many engineering days sit between the decision to leave and the first correct run afterwards.\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Ch2 id=\"2-why-the-ingestion-layer-accumulates-it-faster-than-anything-else\">2. Why the ingestion layer accumulates it faster than anything else\u003C/h2>\u003Cp>Three properties combine here that do not combine anywhere else in the stack:\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>You add connectors one at a time, and each one is small:\u003C/strong> Nobody convenes a review to add the 14th source. The decision is made by whoever had the ticket, in an afternoon, using whatever the platform makes easy. Fifty afternoons later the accumulation is significant and no single afternoon was wrong.\u003C/li>\u003Cli>\u003Cstrong>Every connector encodes decisions that nobody records:\u003C/strong> What happens to a row with no primary key. Whether a field that arrives as a string where it was an object gets coerced, rejected, or split. How nested arrays are flattened. What counts as a failed record. Each of these is a semantic choice with downstream consequences, and when a managed platform makes it for you, it is made silently and correctly according to that platform's opinion.\u003C/li>\u003Cli>\u003Cstrong>The decisions become load-bearing without anyone deciding they should:\u003C/strong> The moment a dashboard, a model, or a finance report depends on a number, the policy that produced that number is frozen. Changing it later is not a refactor, it is a discrepancy someone has to explain.\u003C/li>\u003C/ul>\u003Cp>Put together: the ingestion layer is where your business logic goes to hide. Which is another way of saying the ingestion layer is where the decisions with consequences live: if you cannot read them, you do not own them.\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Ch2 id=\"3-the-inventory-and-the-answer-you-do-not-want\">3. The inventory, and the answer you do not want\u003C/h2>\u003Cp>Run those five questions against your own stack, honestly, and the total tells you what you would rewrite.\u003C/p>\n\u003C!--kg-card-begin: html-->\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>What\u003C/th>\n\u003Cth>The portable answer\u003C/th>\n\u003Cth>The answer that becomes a rewrite\u003C/th>\n\u003C/tr>\n\u003C/thead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Connectors\u003C/strong>\u003C/td>\n\u003Ctd>Source code you hold, in a repository you control\u003C/td>\n\u003Ctd>Configuration inside a vendor's catalogue\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Transformations\u003C/strong>\u003C/td>\n\u003Ctd>Portable code or SQL you could run elsewhere\u003C/td>\n\u003Ctd>Expressed in a proprietary runtime's dialect\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Quality rules\u003C/strong>\u003C/td>\n\u003Ctd>Version-controlled next to the code\u003C/td>\n\u003Ctd>Clicked into a user interface\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Orchestration\u003C/strong>\u003C/td>\n\u003Ctd>A definition you own and could execute elsewhere\u003C/td>\n\u003Ctd>A managed scheduler with no meaningful export\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Lineage and history\u003C/strong>\u003C/td>\n\u003Ctd>Reproducible from the repository\u003C/td>\n\u003Ctd>Visible only in a vendor console\u003C/td>\n\u003C/tr>\n\u003C/tbody>\n\u003C/table>\n\u003C!--kg-card-end: html-->\n\u003Cp>Every row where your honest answer is in the right-hand column is work you would do again. Count it in engineering days. That total, not the contract, is your switching cost.\u003C/p>\u003Cp>Two things usually surprise people running this for the first time. The connectors are rarely the biggest number, because a connector is a bounded piece of work. The quality rules are, because they accumulated over years, they were never written down anywhere else, and the person who knew why rule 31 exists has left.\u003C/p>\u003Cp>&nbsp;\u003C/p>
1355\n\u003Ch2 id=\"4-three-things-that-look-like-portability-and-are-not\">4. Three things that look like portability and are not\u003C/h2>\u003Cp>Each of these appears in vendor material and each is true in a narrow sense that does not survive contact with an actual migration.\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>\"We use open formats:\"\u003C/strong> Your storage is portable. Parquet is Parquet and Iceberg is Iceberg. The logic that wrote into it is not, and the logic is what you would rebuild. Open storage formats solve the problem that was already easiest.\u003C/li>\u003Cli>\u003Cstrong>\"You can export your configuration:\"\u003C/strong> A YAML file you cannot execute anywhere is a description of your pipeline, not your pipeline. Ask what runs it after the export. If the answer is only the product you are leaving, you have exported a document.\u003C/li>\u003Cli>\u003Cstrong>\"It is open source:\"\u003C/strong> Self-hostable and portable are different properties. An open-source connector still targets that project's interfaces, its state model, its idea of what a sync is. You have removed the licence risk and kept the rewrite. That is worth something, and it is worth less than it sounds.\u003C/li>\u003C/ul>\u003Cp>The test that cuts through all three: name the runtime you would move to, and ask what percentage of the artifact runs there unmodified.\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Ch2 id=\"5-what-changes-in-january-2027-and-what-it-does-not-fix\">5. What changes in January 2027, and what it does not fix\u003C/h2>\u003Cp>The EU Data Act makes portability a binding right rather than a commercial courtesy. Its switching provisions have applied since September 2025, and from 12 January 2027 providers are generally no longer permitted to charge for switching, including data egress and transit charges.\u003C/p>\u003Cp>This is a real change and it is worth planning around. It is also narrower than the headlines suggest, for a reason that follows directly from section 1.\u003C/p>\u003Cp>The law removes the fee. It does not remove the rewrite.\u003C/p>\u003Cp>After January 2027 your exit gets cheaper in euros and stays exactly as expensive in engineering days. A right to leave is worth what your technical ability to exercise it is worth, and that ability is decided by choices you already made, one connector at a time, in afternoons nobody reviewed.\u003C/p>\u003Cp>Which is the argument for running the inventory in section 3 now rather than in 2027. The regulation gives you the door. Whether you can walk through it is a property of your own repository.\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Ch2 id=\"6-measuring-your-own-number-in-an-afternoon\">6. Measuring your own number in an afternoon\u003C/h2>\u003Cp>You do not need a full audit, you need an estimate good enough to argue with.\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>Pick your 3 most business-critical sources:\u003C/strong> Not the 3 most complex, the 3 whose numbers appear in front of someone who matters.\u003C/li>\u003Cli>\u003Cstrong>For each, list what you would have to rebuild\u003C/strong> to produce the same output on a different runtime. Connector, transformations, quality rules, schedule, alerting, and the historical backfill.\u003C/li>\u003Cli>\u003Cstrong>Estimate it in engineering days\u003C/strong>, using \u003Ca href=\"https://prettywhale.ai/blog/estimating-data-ingestion-work-a-unit-of-work-method\" rel=\"noopener nofollow\">your own delivery history rather than a benchmark\u003C/a>.\u003C/li>\u003Cli>\u003Cstrong>Multiply by your source count\u003C/strong>, with a discount for the ones that are genuinely simple. The result is wrong in the third digit and right in the order of magnitude, which is all a decision needs.\u003C/li>\u003Cli>\u003Cstrong>Write the number down and date it.\u003C/strong> Then look at it again at the next renewal, which is the only moment anyone has leverage.\u003C/li>\u003C/ul>\u003Cp>The teams that do this find one of two things. Either the number is small, in which case a managed platform is a good deal and they should stop worrying about it. Or the number is large and nobody knew, which is the situation this article is about.\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Ch2 id=\"what-to-take-away\">What to take away\u003C/h2>\u003Cp>Lock-in in a data platform is not a clause you agreed to. It is an accumulation you never measured, built one connector at a time by peo
1355ple making reasonable local decisions.\u003C/p>\u003Cp>The measurement is a rewrite estimate, in engineering days, for the sources that matter. The five inventory questions get you there in an afternoon: where do the connectors live, where do the transformations live, where do the quality rules live, who owns the orchestration definition, and can the history be reproduced from a repository.\u003C/p>\u003Cp>The regulatory change arriving in January 2027 removes the exit fee. It leaves the rewrite untouched, which makes the rewrite the only number worth knowing.\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Chr>\u003Cp>\u003Cem>PrettyWhale.ai produces the ingestion deliverable as portable code the client owns and runs: the pipeline, its tests, its quality transformations, its output schema, its configuration and its documentation. There is no vendor in the data path, no volume meter, and nothing to export, because nothing was ever held elsewhere. The trade is explicit: you own the operation, and in exchange the decisions that shape your data are in a repository you can read.\u003C/em>\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Cdiv class=\"kg-card kg-cta-card kg-cta-bg-white kg-cta-immersive    kg-cta-centered\" data-layout=\"immersive\">\n            \n            \u003Cdiv class=\"kg-cta-content\">\n                \n                \n                    \u003Cdiv class=\"kg-cta-content-inner\">\n                    \n                        \u003Cdiv class=\"kg-cta-text\">\n                            \u003Cp>\u003Cspan style=\"white-space: pre-wrap;\">You have any questions on PrettyWhale.ai ? \u003C/span>\u003C/p>\n                        \u003C/div>\n                    \n                    \n                        \u003Ca href=\"https://prettywhale.ai/contact\" class=\"kg-cta-button \" style=\"background-color: #FF6B1A; color: #FFFFFF;\">\n                            Contact us\n                        \u003C/a>\n                        \n                    \u003C/div>\n                \n            \u003C/div>\n        \u003C/div>","published_at":"2026-08-26T09:46:38.000+00:00","reading_time":5,"feature_image":null,"primary_author":{"id":"69f1af43e6e1d40001b214b8","name":"Kevin BIESSY","slug":"kevin","profile_image":null,"cover_image":null,"bio":null,"website":null,"location":null,"facebook":null,"twitter":null,"threads":null,"bluesky":null,"mastodon":null,"tiktok":null,"youtube":null,"instagram":null,"linkedin":null,"meta_title":null,"meta_description":null,"url":"https://prettywhale.ai/author/kevin/"},"tags":[{"id":"6a4d1ef0399f6a0001a8c991","name":"Data Engineering","slug":"data-engineering","description":null,"feature_image":null,"visibility":"public","og_image":null,"og_title":null,"og_description":null,"twitter_image":null,"twitter_title":null,"twitter_description":null,"meta_title":null,"meta_description":null,"codeinjection_head":null,"codeinjection_foot":null,"canonical_url":null,"accent_color":"#FF6B1A","url":"https://prettywhale.ai/tag/data-engineering/"}]},{"title":"Small Language Models for Code Generation: When Narrow Beats Large","slug":"small-language-models-for-code-generation-when-narrow-beats-large","excerpt":"Small language models can match large ones on a bounded job like code generation, at 10 to 30 times lower serving cost. Here is when that trade holds.","html":"\u003Cp>Most teams building on language models start from the same place: use the best available model, meaning the largest and most capable one the budget allows. It is a sensible starting point and it is almost never revisited, which turns it from a decision into a default.\u003C/p>\u003Cp>There is a serious argument that the default is wrong for a large class of systems, and that \u003Cstrong>small language models\u003C/strong> are the better substrate whenever the job is bounded. Code generation for a single domain is about as bounded as a job gets, which makes it the clearest case to examine. Small model, narrow job.\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Ch2 id=\"1-what-the-research-actually-says\">1. What the research actually says\u003C/h2>\u003Cp>In \u003Ca href=\"https://arxiv.org/abs/2506.02153?ref=prettywhale.ai\" rel=\"noreferrer\">\u003Cem>Small Language Models are the Future of Agentic AI\u003C/em>\u003C/a> (Belcak et al., NVIDIA Research and Georgia Tech, arXiv:2506.02153v2, September 2025), the authors take the position that small models rather than large ones are the right substrate for agentic systems. Their working definition of a small model is practical rather than numeric: one that fits on a common consumer device and answers a single user's request with usable latency. As of 2025 they placed that boundary around 10 billion parameters.\u003C/p>\u003Cp>Three claims carry the argument, and the figures are theirs.\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>Capability is often sufficient:\u003C/strong> They cited Phi-2 at 2.7B reaching commonsense reasoning and code generation scores on par with 30B models while running roughly 15 times faster, Phi-3 small at 7B reaching parity with 70B models on code generation, and distilled 7B reasoning models outperforming large proprietary ones on specific tasks.\u003C/li>\u003Cli>\u003Cstrong>The economics are not close:\u003C/strong> They put serving a 7B model at 10 to 30 times cheaper than a 70 to 175B model in latency, energy and FLOPs. Fine-tuning, they note, becomes a matter of a few GPU-hours rather than a project.\u003C/li>\u003Cli>\u003Cstrong>Breadth is largely wasted:\u003C/strong> Agents exercise a narrow subset of what a general model can do. You pay for the whole capability surface and use a corner of it.\u003C/li>\u003C/ul>\u003Cp>They also quantify the opportunity on real systems, estimating that roughly 60% of the model calls in MetaGPT, 40% in Open Operator and 70% in Cradle could be handled by specialized small models.\u003C/p>\u003Cp>The point of citing this is not that a paper settles the question. It is that the default deserves an argument, and there is a serious one on the other side.\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Ch2 id=\"2-why-code-generation-is-the-strongest-case-for-a-small-model\">2. Why code generation is the strongest case for a small model\u003C/h2>\u003Cp>Among the paper's arguments, one applies to code generation with more force than to anything else, and it is the one least discussed.\u003C/p>\u003Cp>Agentic systems need \u003Cstrong>strict format adherence\u003C/strong>. A tool call has to be exactly well-formed. Generated code has to parse, satisfy an interface, and fit a project structure. The authors argue that models specialized for a single output format outperform generalists at producing it reliably, and that this reliability matters more in these systems than nuanced understanding does.\u003C/p>\u003Cp>That maps onto code generation better than onto conversation. When the output is a runnable project, \"usually correct in shape\" is a failure mode rather than a quality level. A missing import, a signature that does not match the interface, a test that references a method the code does not expose, and the deliverable does not run. The consequence of a formatting near-miss is total rather than graded.\u003C/p>\u003Cp>The corollary is uncomfortable for the default: a larger model with broader knowledge and a slightly less reliable grip on your exact output shape is worse for this job than a smaller one that has only ever produced that shape.\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Ch2 id=\"3-a-domain-narrow-enough-to-generate-is-narrow-enough-to-train-for\">3. A domain narrow enough to generate is narrow enough to train for\u003C/h2>\u003Cp>This is where the two questions turn out to be one.\u003C/p>\u003Cp>We wrote elsewhere about what makes a domain narrow enough for specialized generation: structural repetition, a definition of done you can write, mechanical verification, and a boun
1355ded vocabulary of variation.\u003C/p>\u003Cp>Those same four properties are what make a domain trainable with a small model.\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>Structural repetition\u003C/strong> gives you a training target that recurs, so the model learns a shape rather than a distribution of possibilities.\u003C/li>\u003Cli>\u003Cstrong>A writable definition of done\u003C/strong> gives you a label. You can tell whether an output is correct, which is the prerequisite for improving on it systematically.\u003C/li>\u003Cli>\u003Cstrong>Mechanical verification\u003C/strong> gives you an automated reward signal, and the ability to reject bad outputs without a human in the loop.\u003C/li>\u003Cli>\u003Cstrong>A bounded vocabulary of variation\u003C/strong> bounds the capability surface you actually need, which is the entire argument for not paying for the rest.\u003C/li>\u003C/ul>\u003Cp>A domain that fails those tests needs a general model, because you cannot specify what you want well enough to specialize for it. A domain that passes them makes the large model an expensive way to buy capability you will not use.\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Ch2 id=\"4-what-you-give-up\">4. What you give up\u003C/h2>\u003Cp>The trade is real and we can state it:\u003C/p>\n\u003C!--kg-card-begin: html-->\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>What you lose\u003C/th>\n\u003Cth>Consequence\u003C/th>\n\u003C/tr>\n\u003C/thead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Breadth\u003C/strong>\u003C/td>\n\u003Ctd>Outside the domain, the model does nothing useful. There is no graceful degradation into general helpfulness\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>The novel case\u003C/strong>\u003C/td>\n\u003Ctd>A source or a requirement genuinely unlike anything in the domain gets handled worse than a general model would handle it\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Conversational latitude\u003C/strong>\u003C/td>\n\u003Ctd>You cannot ask it to explain, to refactor for style, or to answer a question about the code. Those are different jobs\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>The escape hatch\u003C/strong>\u003C/td>\n\u003Ctd>With a general model, an unanticipated request produces something. With a specialist, it will probably produces nothing useful, and the system has to route elsewhere\u003C/td>\n\u003C/tr>\n\u003C/tbody>\n\u003C/table>\n\u003C!--kg-card-end: html-->\n\u003Cp>The last row is the one to design for rather than to argue away. The paper's own answer is heterogeneity: different models at different decision points, a specialist where the format matters and a general model where the request is open-ended. That is an architecture choice, not a compromise, and it is what makes the migration incremental rather than a bet.\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Ch2 id=\"5-how-you-would-test-this-yourself\">5. How you would test this yourself\u003C/h2>\u003Cp>The paper proposes a six-step path, and it is worth reading as an evaluation method rather than as a migration plan. Condensed, with the parts that matter for a team considering this:\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>Log what you actually send:\u003C/strong> Prompts, responses, tool calls, latency. Most teams running on a general model have no record of the distribution of requests they make, which means they cannot tell how much breadth they are using.\u003C/li>\u003Cli>\u003Cstrong>Cluster the requests:\u003C/strong> Unsupervised clustering on the prompts tells you how many distinct jobs you really have. The answer is usually far smaller than the capability you are paying for.\u003C/li>\u003Cli>\u003Cstrong>Take the largest cluster and only that one:\u003C/strong> Fine-tune a candidate small model on it. The paper puts the useful dataset size at 10,000 to 100,000 examples and the technique at LoRA or full fine-tuning depending on scale.\u003C/li>\u003Cli>\u003Cstrong>Compare on your own acceptance criteria\u003C/strong>, not on public benchmarks. What matters is whether the output clears the bar you would apply to a colleague's work.\u003C/li>\u003C/ul>\u003Cp>The first two steps cost days rather than quarters, and they answer the question that decides the rest: how much of your general model's range are you actually exercising.\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Ch2 id=\"6-the-cost-question-a-cto-will-actually-ask\">6. The cost question a CTO will actually ask\u003C/h2>\u003Cp>The 10 to 30 times figure is about serving. It is not the number that decides anything, because nobody's bill is only inference.\u003C/p>\u003Cp>
1355Four cost lines move in opposite directions when you go from an API call to a model you run:\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>Inference goes down\u003C/strong>, by roughly the factor above, and it keeps going down as volume grows because you are no longer paying per token.\u003C/li>\u003Cli>\u003Cstrong>Operations goes up:\u003C/strong> Somebody hosts, monitors, patches and capacity-plans a model. That line did not exist before and it does not shrink with volume.\u003C/li>\u003Cli>\u003Cstrong>Iteration cost goes down:\u003C/strong> The paper puts fine-tuning at a few GPU-hours, which changes what you are willing to try. A model you can retrain in an afternoon gets improved. A model you can only prompt gets worked around.\u003C/li>\u003Cli>\u003Cstrong>Switching cost goes up:\u003C/strong> A specialized model is an asset you own and also a commitment. Changing your mind means retraining rather than changing a string.\u003C/li>\u003C/ul>\u003Cp>Which way the sum falls depends almost entirely on volume and on whether the operations line is already staffed. At low volume with no platform team, the API call is correct and the argument in this article does not apply to you. At high volume on a narrow job, the crossover arrives sooner than most teams assume, and the iteration line is the one usually left out of the comparison.\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Ch2 id=\"7-where-the-argument-is-weakest\">7. Where the argument is weakest\u003C/h2>\u003Cp>The authors are explicit about the counter-arguments:\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>Economy of scale is a real objection:\u003C/strong> Centralized endpoints amortize infrastructure across enormous load, and the authors concede the economics are highly case-specific. Self-hosting a small model shifts cost from a per-token line to an operations line, and that trade is not automatically favourable.\u003C/li>\u003Cli>\u003Cstrong>No timeline is offered:\u003C/strong> They explicitly decline to predict when adoption happens, and they name the barriers: tens of billions already invested in centralized hosting, benchmarks that measure generalist ability rather than agentic reliability, and simple lack of attention.\u003C/li>\u003Cli>\u003Cstrong>Not everything is replaceable:\u003C/strong> Their own framing keeps general models in the architecture for the open-ended parts. The claim is about the majority of calls, not all of them.\u003C/li>\u003C/ul>\u003Cp>&nbsp;\u003C/p>\n\u003Ch2 id=\"what-to-take-away\">What to take away\u003C/h2>\u003Cp>Reaching for the largest available model is a default, and defaults deserve one examination.\u003C/p>\u003Cp>The examination is cheap: log the requests you actually make, cluster them, and see how many distinct jobs there are. If the answer is a handful, you are paying for a capability surface you are not using, and the research suggests the specialized alternative is 10 to 30 times cheaper to serve at comparable quality on those jobs.\u003C/p>\u003Cp>The condition is that your domain has to be specifiable. If you cannot write down what a correct output looks like, you cannot specialize, and the general model is the honest tool. If you can, the size of the model stops being a proxy for the quality of the result.\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Chr>\u003Cp>\u003Cem>PrettyWhale.ai is an Engineering AI built on a specialized small language model trained for one job: generate ingestion and integration code. You give it one sample of a data source and it returns a complete production-ready pipeline : executable code, unit tests, data quality checks, schemas, configuration and documentation. One bundle with every artifacts, the same shape every time, in standard languages.\u003C/em>\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Cdiv class=\"kg-card kg-cta-card kg-cta-bg-white kg-cta-immersive    kg-cta-centered\" data-layout=\"immersive\">\n            \n            \u003Cdiv class=\"kg-cta-content\">\n                \n                \n                    \u003Cdiv class=\"kg-cta-content-inner\">\n                    \n                        \u003Cdiv class=\"kg-cta-text\">\n                            \u003Cp>\u003Cspan style=\"white-space: pre-wrap;\">You have any questions on PrettyWhale.ai ? \u003C/span>\u003C/p>\n                        \u003C/div>\n                    \n                    \n                        \u003Ca href=\"https://prettywhale.ai/contact\" class=\"kg-cta-button \" style=\"background-color: #FF6B1A;
1355 color: #FFFFFF;\">\n                            Contact us\n                        \u003C/a>\n                        \n                    \u003C/div>\n                \n            \u003C/div>\n        \u003C/div>","published_at":"2026-08-20T09:27:33.000+00:00","reading_time":6,"feature_image":null,"primary_author":{"id":"69f1af43e6e1d40001b214b8","name":"Kevin BIESSY","slug":"kevin","profile_image":null,"cover_image":null,"bio":null,"website":null,"location":null,"facebook":null,"twitter":null,"threads":null,"bluesky":null,"mastodon":null,"tiktok":null,"youtube":null,"instagram":null,"linkedin":null,"meta_title":null,"meta_description":null,"url":"https://prettywhale.ai/author/kevin/"},"tags":[{"id":"6a4d1f89399f6a0001a8c99b","name":"Artificial Intelligence","slug":"artificial-intelligence","description":null,"feature_image":null,"visibility":"public","og_image":null,"og_title":null,"og_description":null,"twitter_image":null,"twitter_title":null,"twitter_description":null,"meta_title":null,"meta_description":null,"codeinjection_head":null,"codeinjection_foot":null,"canonical_url":null,"accent_color":"#FFC13C","url":"https://prettywhale.ai/tag/artificial-intelligence/"}]},{"title":"Who Accepts AI-Generated Code That Nobody Wrote","slug":"who-accepts-code-nobody-wrote","excerpt":"Code review assumes an author who can answer for the code. Generated code breaks that assumption. What changes in the pull request, and after an incident.","html":"\u003Cp>Code review works on an assumption nobody states. Somewhere there is an author, and the author can answer for the code: why this approach, what happens in that case, what they considered and rejected. The reviewer's job is to interrogate that reasoning, and the author's ability to defend it is most of what makes approval mean something.\u003C/p>\u003Cp>Generated code removes the author. The person submitting it can tell you what they asked for. They often cannot tell you why line 47 handles the case that way, because nobody decided it.\u003C/p>\u003Cp>That does not make the code worse. It makes approval rest on something that has to be named, because the old basis is gone and nothing replaced it by default.\u003C/p>\u003Ch2 id=\"1-three-bases-for-ai-generated-code-acceptance\">1. Three bases for AI-generated code acceptance\u003C/h2>\u003Cp>There are only three answers to who accepts, and each has a cost. Choosing deliberately beats discovering which one you drifted into.\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>The prompter accepts:\u003C/strong> Whoever ran the generation owns the output. Simple, matches how pull requests already work, and it is what most teams do without deciding to.The problem is that it asks for accountability without the means. A person who cannot answer for line 47 is being asked to stand behind it anyway, and the predictable result is either genuine risk transfer to someone who cannot carry it, or a quiet fiction everyone maintains.\u003C/li>\u003Cli>\u003Cstrong>The reviewer accepts:\u003C/strong> The approver owns it, on the basis of having read it.Honest, and expensive in the one resource you have least of. It also runs into the completeness problem: a finished-looking deliverable of 20 files gets skimmed, not read, and the approval means less than it appears to.\u003C/li>\u003Cli>\u003Cstrong>The check accepts:\u003C/strong> The deliverable is accepted because it satisfies a specified set of properties, verified mechanically. A person owns the specification and owns the decision to trust it, rather than owning each line.This is the only one that scales, and it relocates accountability rather than dissolving it. Somebody wrote the contract, somebody decided it was sufficient, and both of those are answerable positions.\u003C/li>\u003C/ul>\u003Cp>
1355Most mature arrangements combine the third for the mechanical properties and the second for the judgement, which is a division of labour rather than a compromise.\u003C/p>\u003Ch2 id=\"2-what-changes-in-the-pull-request\">2. What changes in the pull request\u003C/h2>\u003Cp>Four practical shifts, and the first one is the whole article in a sentence.\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>The question moves from \"why did you do it this way\" to \"what did you verify\":\u003C/strong> The first is increasingly unanswerable and the second is always answerable. It is also the better question, because it produces a checkable claim rather than a narrative.\u003C/li>\u003Cli>\u003Cstrong>The description carries the verification, not the reasoning:\u003C/strong> What was generated, from what input, which checks ran, what the reviewer looked at by hand. That is a record you can consult after an incident, which a rationale is not.\u003C/li>\u003Cli>\u003Cstrong>Size stops indicating effort:\u003C/strong> Reviewers have always used volume as a proxy for how much care went in, and therefore for how carefully to read. A 900-line change produced in 20 minutes breaks the proxy. Read by risk instead: the boundary with your system, the defaults, the error paths.\u003C/li>\u003Cli>\u003Cstrong>The presence of tests stops being evidence:\u003C/strong> It used to signal that somebody thought about failure modes, because writing them forced it. Now it signals that tests were requested. What they assert is the only thing that carries information.\u003C/li>\u003C/ul>\u003Ch2 id=\"3-the-question-that-arrives-after-an-incident\">3. The question that arrives after an incident\u003C/h2>\u003Cp>This is the part worth settling before you need it, because the conversation goes badly when it is improvised.\u003C/p>\u003Cp>A pipeline built from generated code corrupts a month of data. In the review, the meeting or the postmortem, somebody asks who approved this.\u003C/p>\u003Cp>Under the first basis, the answer is a person who could not have caught it. That is an unfair outcome and it produces the wrong lesson: people become reluctant to submit generated work, or they submit it with a disclaimer, and neither improves anything.\u003C/p>\u003Cp>Under the third basis, the answer is different and more useful. The deliverable satisfied the contract, and the contract did not cover the failure. That points at a gap in the specification, which is fixable, rather than at a lapse in attention, which is not.\u003C/p>\u003Cp>The second answer is also the honest one in most real cases. Reviewers rarely catch the defect that causes the incident. They catch the ones that are visible, and the incident is almost always caused by something that was not.\u003C/p>\u003Cp>Two things follow from choosing the third basis deliberately.\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>The contract has to be owned by a named person:\u003C/strong> An unowned specification decays, and after an incident an unowned specification means nobody is accountable, which is worse than the situation you started from.\u003C/li>\u003Cli>\u003Cstrong>A gap found by an incident becomes a clause:\u003C/strong> That is the loop that makes the arrangement improve. Without it, the contract is a snapshot of what you knew before your first serious failure.\u003C/li>\u003C/ul>\u003Ch2 id=\"4-what-to-write-down-now\">4. What to write down now\u003C/h2>\u003Cp>Four things, and none of them takes longer than a meeting.\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>Which basis you are using\u003C/strong>, per category of work. Generated ingestion code might be accepted on checks. A change to authentication might require a reviewer who read every line. Both are defensible, and the mistake is having no stated answer, because then the answer is whichever one is convenient after the fact.\u003C/li>\u003Cli>\u003Cstrong>Who owns the contract:\u003C/strong> One name, with time allocated to maintain it.\u003C/li>\u003Cli>\u003Cstrong>What the pull request has to state:\u003C/strong> What was generated, from what, which checks ran, what was reviewed by hand. Four lines in a template.\u003C/li>\u003Cli>\u003Cstrong>What the reviewer is still responsible for:\u003C/strong>
1355 Explicitly, so it is small and real rather than implicit and unbounded. Usually: the boundary with your system, the defaults, whether the thing solves the right problem.\u003C/li>\u003C/ul>\u003Cp>That last item is worth protecting. A reviewer with a short, explicit, genuinely important list does that job well. A reviewer nominally responsible for everything does none of it, and everybody knows it.\u003C/p>\u003Ch2 id=\"what-to-take-away\">What to take away\u003C/h2>\u003Cp>Review used to rest on an author who could answer for the code. Generated code removes the author, and the basis for approval has to be replaced rather than assumed.\u003C/p>\u003Cp>Three bases exist. The prompter, which asks for accountability without the means. The reviewer, which is honest and does not scale. The check, which scales and moves accountability to whoever wrote and accepted the specification.\u003C/p>\u003Cp>Pick one per category of work, name the owner of the contract, and shorten what the reviewer is responsible for until it is a list they can actually honour. Then change the question in review from why you did it this way to what you verified.\u003C/p>\u003Cp>The second question is answerable, checkable, and it was always the better one.\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Chr>\u003Cp>\u003Cem>PrettyWhale.ai produces artifacts an acceptance test needs: a full test suite, a machine-readable output schema, documentation of the failure behaviour. The point is not that review disappears. It is that approval can rest on verified properties rather than on a rationale nobody can supply.\u003C/em>\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Cdiv class=\"kg-card kg-cta-card kg-cta-bg-white kg-cta-immersive    kg-cta-centered\" data-layout=\"immersive\">\n            \n            \u003Cdiv class=\"kg-cta-content\">\n                \n                \n                    \u003Cdiv class=\"kg-cta-content-inner\">\n                    \n                        \u003Cdiv class=\"kg-cta-text\">\n                            \u003Cp>\u003Cspan style=\"white-space: pre-wrap;\">You have any questions on PrettyWhale.ai ? \u003C/span>\u003C/p>\n                        \u003C/div>\n                    \n                    \n                        \u003Ca href=\"https://prettywhale.ai/contact\" class=\"kg-cta-button \" style=\"background-color: #FF6B1A; color: #FFFFFF;\">\n                            Contact us\n                        \u003C/a>\n                        \n                    \u003C/div>\n                \n            \u003C/div>\n        \u003C/div>","published_at":"2026-08-17T12:11:09.000+00:00","reading_time":4,"feature_image":null,"primary_author":{"id":"69f1af43e6e1d40001b214b8","name":"Kevin BIESSY","slug":"kevin","profile_image":null,"cover_image":null,"bio":null,"website":null,"location":null,"facebook":null,"twitter":null,"threads":null,"bluesky":null,"mastodon":null,"tiktok":null,"youtube":null,"instagram":null,"linkedin":null,"meta_title":null,"meta_description":null,"url":"https://prettywhale.ai/author/kevin/"},"tags":[{"id":"6a4d1f89399f6a0001a8c99b","name":"Artificial Intelligence","slug":"artificial-intelligence","description":null,"feature_image":null,"visibility":"public","og_image":null,"og_title":null,"og_description":null,"twitter_image":null,"twitter_title":null,"twitter_description":null,"meta_title":null,"meta_description":null,"codeinjection_head":null,"codeinjection_foot":null,"canonical_url":null,"accent_color":"#FFC13C","url":"https://prettywhale.ai/tag/artificial-intelligence/"}]},{"title":"Production-Ready Data Pipelines: What It Means and Who Decides","slug":"what-production-ready-means-for-a-data-pipeline-and-who-decides","excerpt":"Ask two engineers when an ingestion pipeline is finished and you get two answers. Here is how to write the bar down on one page and enforce it in review.","html":"\u003Cp>Ask two engineers on the same team when an ingestion pipeline is finished.\u003C/p>\u003Cp>The first says it is finished when it runs correctly on the source. The second says it is finished when it runs correctly on the source, handles malformed records, has tests, has a documented output contract, and can be picked up by someone else. Both answers are reasonable. They differ by a factor of three in effort and by an order of magnitude in maintenance cost.\u003C/p>\u003Cp>The disagreement is rarely had out loud, which is why it produces a different pipeline every time depending on who was assigned and how much sc
1355hedule pressure they were under. Writing it down is a one-page document and a meeting. It is also the cheapest intervention available on delivery quality, and the one most often skipped in favour of buying a tool.\u003C/p>\u003Ch2 id=\"1-what-the-bar-actually-is-artefact-by-artefact\">1. What the bar actually is, artefact by artefact\u003C/h2>\u003Cp>A definition of done that says \"code, tests and documentation\" is not a definition, because every one of those words means something different to each reader. The bar has to be specific enough that two people can look at the same pipeline and agree on whether it clears.\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>Code:\u003C/strong> runs on the full source, not on a sample. Handles the failure modes named in your team's list. Configuration externalized rather than hardcoded.\u003C/li>\u003Cli>\u003Cstrong>Tests:\u003C/strong> every transformation has a test for a valid input, a null input, and a malformed input. The malformed case asserts the behaviour, not just the absence of a crash. Coverage target stated as a number your team agreed on.\u003C/li>\u003Cli>\u003Cstrong>Data quality checks:\u003C/strong> completeness, uniqueness of the claimed key, value ranges, expected volume. Failures recorded per record rather than logged and forgotten.\u003C/li>\u003Cli>\u003Cstrong>Output contract:\u003C/strong> a machine-readable schema of what the pipeline emits, checked in CI. Nullable fields state the conditions under which they are null.\u003C/li>\u003Cli>\u003Cstrong>Configuration:\u003C/strong> every path, threshold, credential reference and error policy lives outside the code.\u003C/li>\u003Cli>\u003Cstrong>Orchestration:\u003C/strong> schedule, dependencies, retry policy, and what happens on failure, including who is notified.\u003C/li>\u003Cli>\u003Cstrong>Documentation:\u003C/strong> answers what it reads, what it produces, what happens to a bad record, what happens if it runs twice, and how to add a field.\u003C/li>\u003C/ul>\u003Ch2 id=\"2-who-decides-when-the-engineer-and-the-manager-disagree\">2. Who decides when the engineer and the manager disagree\u003C/h2>\u003Cp>This is the part a checklist cannot solve, and it is where most definitions of done quietly die.\u003C/p>\u003Cp>The engineer wants to add the tests. The delivery manager needs the pipeline on Thursday. Both are doing their job. If the answer is decided case by case, the definition of done is decorative, because it loses every time there is pressure, and there is always pressure.\u003C/p>\u003Cp>Three arrangements work, in ascending order of maturity:\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>The bar is not negotiable, the scope is:\u003C/strong> The definition of done applies to whatever ships. If Thursday is immovable, fewer sources ship on Thursday. This is the cleanest rule and the hardest to hold the first few times.\u003C/li>\u003Cli>\u003Cstrong>Named exceptions with an expiry:\u003C/strong> Something ships below the bar, the gap is written down with a date, and the date is tracked like any other commitment. Honest, and it works as long as the list gets reviewed. If nobody reviews it, this is just the previous arrangement with extra paperwork.\u003C/li>\u003Cli>\u003Cstrong>A tech lead with the final call:\u003C/strong> One person owns the bar and can say no. Requires that the organization actually backs them the first time they use it, which is the real test.\u003C/li>\u003C/ul>\u003Cp>What does not work is collective ownership with no arbitrator. When everyone owns the bar, the bar is set by whoever is most tired that week.\u003C/p>\u003Cp>One framing that helps in the conversation with delivery: the bar is not a quality preference, it is a cost decision about a later quarter. Shipping below it moves work forward in the calendar and multiplies it. That argument lands better than an appeal to craftsmanship, because it is in the currency the other person is managing.\u003C/p>\u003Ch2 id=\"3-building-your-teams-failure-mode-list\">3. Building your team's failure-mode list\u003C/h2>\u003Cp>The generic bar above applies anywhere. The part that makes a definition of done yours is the list of failure modes your sources actually produce, and it has to come from your own incidents rather than from an article.\u003C/p>\u003Cp>Build it once, in an hour:\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>Take the last 10 production incidents\u003C/strong> on ingestion: Tickets, alerts, or the memory of whoever was on call.\u003C/li>\u003Cli>\u003Cstrong>For each, write the one sentence that describes what the code should have done:\u003C/strong> Not what went wrong, what the handling should have been. \"A record with an unparseable date should have been flagged and skipped, not aborted the batch.\"\u003C/li>\u003Cli>\u003Cstrong>Group them:\u003C/strong> Most teams end up with 5 to 8 distinct modes, and the same three account for the majority of incidents.\u003C/li>\u003Cli>\u003Cstrong>Put the list in the definition of done\u003C/strong>, as the things every pipeline is expected to handle.\u003C/li>\u003C/ul>\u003Cp>Two properties make this list valuable in a way a generic checklist is not. It is specific to the sources you actually ingest, so nobody can argue it is theoretical. And it is short, because your incidents cluster, which means the bar stays achievable.\u003C/p>\u003Cp>Review it after each incident that is not on the list. Either the mode is new and it joins, or the handling was there and failed, which is a different problem.\u003C/p>
1355\u003Ch2 id=\"4-writing-it-on-one-page\">4. Writing it on one page\u003C/h2>\u003Cp>Longer than a page and it stops being read. The structure that holds:\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>The list of artefacts\u003C/strong>, with the specific bar for each, in the form of section 1 rather than in the form of adjectives.\u003C/li>\u003Cli>\u003Cstrong>The failure modes your sources actually produce\u003C/strong>, named. Every team has its own list, and it comes from your incident history rather than from a generic article. Five to eight items.\u003C/li>\u003Cli>\u003Cstrong>What is explicitly out of scope\u003C/strong>, which prevents the definition from expanding into an unbounded quality manifesto.\u003C/li>\u003Cli>\u003Cstrong>The exception process\u003C/strong>, in two sentences.\u003C/li>\u003C/ul>\u003Cp>Write it with the two engineers whose work the team already respects, not alone. A standard extracted from practice gets adopted. A standard handed down gets routed around, and the technical content is often identical in both cases.\u003C/p>\u003Cp>Date it, and put a name on it. An undated document with no owner decays into folklore within a year.\u003C/p>\u003Ch2 id=\"5-enforcing-it-in-review-not-in-documentation\">5. Enforcing it in review, not in documentation\u003C/h2>\u003Cp>A definition of done that lives only in a wiki has no mechanism. The enforcement has to sit in the path the work already takes:\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>Pull request template\u003C/strong> that lists the artefacts as checkboxes: Cheap, visible, and it makes the omission deliberate rather than accidental.\u003C/li>\u003Cli>\u003Cstrong>CI checks\u003C/strong> for the parts a machine can verify: tests pass, coverage threshold met, schema validates against a sample output.\u003C/li>\u003Cli>\u003Cstrong>Review as the human gate\u003C/strong> for the parts it cannot: whether the tests assert anything meaningful, whether the documentation answers the five questions, whether the failure modes on your list are actually handled.\u003C/li>\u003C/ul>\u003Cp>The CI part matters more than it looks. Anything a machine checks stops being a conversation, which removes it from the negotiation entirely and leaves the human review free for the judgement calls.\u003C/p>\u003Ch2 id=\"6-what-happens-the-first-month\">6. What happens the first month\u003C/h2>\u003Cp>Two things, and both are worth expecting out loud before they happen.\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>Delivery slows down:\u003C/strong> The team is learning the bar while delivering against it, and the first few pipelines take longer. If nobody says this in advance, the slowdown becomes the evidence that the initiative failed.\u003C/li>\u003Cli>\u003Cstrong>Somebody tests the exception process immediately:\u003C/strong> That first exception decides whether the definition is real. Granting it correctly, with a written gap and a date that gets tracked, establishes the mechanism. Granting it informally establishes that the bar is optional.\u003C/li>\u003C/ul>\u003Cp>After that, the visible effect is not that pipelines get better in a way anyone notices. It is that they stop varying. Estimates start holding, review gets faster because reviewers know what they are looking for, and the pipeline written by whoever was available looks like the pipeline written by anyone else.\u003C/p>\u003Ch2 id=\"what-to-take-away\">What to take away\u003C/h2>\u003Cp>Two engineers finishing the same work at different points is not a discipline problem. It is a specification problem, and the specification is one page.\u003C/p>\u003Cp>Write the bar artefact by artefact, in terms specific enough that two people agree on whether it clears. Decide in advance who arbitrates under pressure, because the answer decided case by case is always the same one. Enforce it where the work already passes, in the pull request and in CI, rather than in a document nobody opens.\u003C/p>\u003Cp>The teams that skip this and buy tooling instead end up with the same variance, applied faster.\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Chr>\u003Cp>\u003Cem>PrettyWhale.ai produces the full artefact set from a source sample: code, unit tests, data quality checks, output schema, configuration, orchestration and documentation. The definition of done stops being something to enforce and becomes what arrives by default. The bar still has to be written, because somebody has to review the output against it.\u003C/em>\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Cdiv class=\"kg-card kg-cta-card kg-cta-bg-white kg-cta-immersive    kg-cta-centered\" data-layout=\"immersive\">\n            \n            \u003Cdiv class=\"kg-cta-content\">\n                \n                \n                    \u003Cdiv class=\"kg-cta-content-inner\">\n                    \n                        \u003Cdiv class=\"kg-cta-text\">\n                            \u003Cp>\u003Cspan style=\"white-space: pre-wrap;\">You have any questions on PrettyWhale.ai ? \u003C/span>\u003C/p>\n                        \u003C/div>\n                    \n                    \n                        \u003Ca href=\"https://prettywhale.ai/contact\" class=\"kg-cta-button \" style=\"background-color: #FF6B1A;
1355 color: #FFFFFF;\">\n                            Contact us\n                        \u003C/a>\n                        \n                    \u003C/div>\n                \n            \u003C/div>\n        \u003C/div>","published_at":"2026-08-12T11:37:47.000+00:00","reading_time":5,"feature_image":null,"primary_author":{"id":"69f1af43e6e1d40001b214b8","name":"Kevin BIESSY","slug":"kevin","profile_image":null,"cover_image":null,"bio":null,"website":null,"location":null,"facebook":null,"twitter":null,"threads":null,"bluesky":null,"mastodon":null,"tiktok":null,"youtube":null,"instagram":null,"linkedin":null,"meta_title":null,"meta_description":null,"url":"https://prettywhale.ai/author/kevin/"},"tags":[{"id":"6a4d1ef0399f6a0001a8c991","name":"Data Engineering","slug":"data-engineering","description":null,"feature_image":null,"visibility":"public","og_image":null,"og_title":null,"og_description":null,"twitter_image":null,"twitter_title":null,"twitter_description":null,"meta_title":null,"meta_description":null,"codeinjection_head":null,"codeinjection_foot":null,"canonical_url":null,"accent_color":"#FF6B1A","url":"https://prettywhale.ai/tag/data-engineering/"}]},{"title":"Estimating Data Ingestion Work: A Unit-of-Work Method","slug":"estimating-data-ingestion-work-a-unit-of-work-method","excerpt":"Stop estimating ingestion tasks one by one. Estimate units of work, build a tier grid from your own delivery history, and recalibrate it after every project.","html":"\u003Cblockquote>Software estimation is hard everywhere. Data estimation is hard in a specific way that generic estimation advice does not address, and that specificity is the whole problem.\u003C/blockquote>\u003Cp>On application work, the unknown lives in the scope. You reduce it by asking questions: what should this screen do, what happens on error, who can see it. Discovery is a conversation, and a good analyst can close most of the gap before anyone commits to a number.\u003C/p>\u003Cp>On ingestion work, the unknown lives in the data. No conversation closes it. The client genuinely believes the export is clean, because the person you are talking to has never opened it. You find out in week three, and by then the number is already in a signed document.\u003C/p>\u003Cp>This article is a method for producing ingestion estimates that survive contact with the data. It is aimed at whoever actually writes the estimate.\u003C/p>\u003Ch2 id=\"1-stop-estimating-tasks-start-estimating-units-of-work\">1. Stop estimating tasks, start estimating units of work\u003C/h2>\u003Cp>The common approach is a task breakdown. Read the source, two days. Write the transformations, three days. Tests, one day. Documentation, half a day. Total, six and a half days, add a buffer.\u003C/p>\u003Cp>This fails for a reason worth understanding: the breakdown is a description of the work, not a prediction of it. Every line is an estimate of something nobody has looked at yet, and the errors compound in the same direction, because every unexamined line looks simpler than it is.\u003C/p>\u003Cp>The alternative is to estimate at the level of a unit that recurs. In ingestion, that unit is the source.\u003C/p>\u003Cp>One source, ingested to production standard, is a repeatable deliverable. It has variants, and the variants are classifiable. Once you know what a standard-tier source costs you, an eleven-source project is arithmetic rather than a guess.\u003C/p>\u003Cp>This only works if two things are true. The definition of \"to production standard\" has to be written down and identical across sources, otherwise you are averaging different deliverables. And your reference figures have to come from your own history, because nobody else's productivity applies to your team.\u003C/p>\u003Ch2 id=\"2-building-your-tier-grid-from-your-own-history\">2. Building your tier grid from your own history\u003C/h2>\u003Cp>3 tiers because the grid has to be usable in a conversation with a client who is not going to sit through a taxonomy.\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>Step one: take your last ten to fifteen ingestion deliveries.\u003C/strong> Across clients, same category of work.\u003C/li>\u003Cli>\u003Cstrong>Step two: reconstruct actual effort.\u003C/strong> Not the estimate, the actual. Timesheets if they are trustworthy, otherwise commit history plus a five-minute c
1355onversation with whoever did it. Elapsed engineering effort from source access to production, excluding client waiting time.\u003C/li>\u003Cli>\u003Cstrong>Step three: sort them by effort\u003C/strong>, then look for the natural breaks. There usually are some, and they usually correspond to the characteristics below.\u003C/li>\u003C/ul>\n\u003C!--kg-card-begin: html-->\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Tier\u003C/th>\n\u003Cth>Characteristics\u003C/th>\n\u003Cth>Your reference effort\u003C/th>\n\u003C/tr>\n\u003C/thead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Simple\u003C/strong>\u003C/td>\n\u003Ctd>Documented stable format, fixed schema, single entity, modest volume, full refresh acceptable\u003C/td>\n\u003Ctd>Calibrate from your own history\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Standard\u003C/strong>\u003C/td>\n\u003Ctd>Pagination or incremental logic needed, some ambiguous types, occasional schema shifts, moderate volume\u003C/td>\n\u003Ctd>Calibrate from your own history\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Complex\u003C/strong>\u003C/td>\n\u003Ctd>Proprietary or legacy source, nested structures, no reliable change indicator, large volumes, or business logic to decode\u003C/td>\n\u003Ctd>Calibrate from your own history\u003C/td>\n\u003C/tr>\n\u003C/tbody>\n\u003C/table>\n\u003C!--kg-card-end: html-->\n\u003Cul>\u003Cli>\u003Cstrong>Step four: write down the classification criteria\u003C/strong>, not just the numbers. In six months someone else will need to classify a source, and if the criteria only exist in your head the grid decays into a lookup table nobody trusts.\u003C/li>\u003C/ul>\u003Cp>The output of this exercise is two hours of work and it is the only credible estimation basis that exists for your team. Published industry benchmarks are worse than useless here, because they average across practices with different definitions of done.\u003C/p>\u003Ch2 id=\"3-the-qualification-phase-in-detail\">3. The qualification phase, in detail\u003C/h2>\u003Cp>This is the part that converts a gamble into an estimate, and it is the part most proposals skip.\u003C/p>\u003Cp>A qualification phase is short and produces a deliverable. Two to five days depending on the number of sources.\u003C/p>\u003Cp>\u003Cstrong>What you do during this phase\u003C/strong>\u003C/p>\u003Cul>\u003Cli>Get real access to every source. Not a specification, not a screenshot. Access.\u003C/li>\u003Cli>Pull a genuine sample from each, large enough to contain the ugly rows. The first hundred records of a file are usually the cleanest hundred records in it.\u003C/li>\u003Cli>Profile each sample: null rates, distinct counts, format variants per field, encoding, volume, and whether the claimed unique identifier is unique.\u003C/li>\u003Cli>Identify the change indicator. Is there a reliable updated-at column, a change log, a sequence. If the answer is no, that source is complex regardless of how simple its format looks.\u003C/li>\u003Cli>Classify each source against your criteria, and record why.\u003C/li>\u003C/ul>\u003Cp>\u003Cstrong>What you deliver\u003C/strong>\u003Cbr>A short document: the inventory of sources with tier and rationale, the profile of each sample, the list of remaining unknowns, and the estimate that follows.\u003C/p>\u003Cp>\u003Cstrong>Why the client could pay for it\u003C/strong>\u003Cbr>Because it has standalone value. It documents their data estate, and it almost always surfaces quality problems they did not know about. More than once the qualification report is the most useful artefact the client receives all quarter, and it is produced before any commitment on the main work.\u003C/p>\u003Cp>\u003Cstrong>The clause that follows from it.\u003C/strong>\u003Cbr>The estimate covers the qualified source, meaning the source as sampled. If what arrives in delivery differs materially from the sample, it gets requalified. Stated up front this reassures rather than alarms, because it demonstrates that you know where the risk lives.\u003C/p>\u003Ch2 id=\"4-what-the-estimate-should-actually-contain\">4. What the estimate should actually contain\u003C/h2>\u003Cp>An estimate that is a single number invites haggling over that number. An estimate that shows its structure moves the conversation to scope.\u003C/p>\n\u003C!--kg-card-begin: html-->\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Component\u003C/th>\n\u003Cth>Why it is a separate line\u003C/th>\n\u003C/tr>\n\u003C/thead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Qualification\u003C/strong>\u003C/td>\n\u003Ctd>Delivered before commitment, has standalone value\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Production, per tier, with counts\u003C/strong>\u003C/td>\n\u003Ctd>
1355Client sees the arithmetic: 4 simple, 5 standard, 2 complex\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Review and acceptance\u003C/strong>\u003C/td>\n\u003Ctd>Real work, and the line that protects your quality bar under pressure\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Contingency, stated explicitly\u003C/strong>\u003C/td>\n\u003Ctd>Hidden buffers get discovered and destroy trust. A named contingency gets discussed\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Explicit exclusions\u003C/strong>\u003C/td>\n\u003Ctd>New sources, historical backfill beyond a stated window, downstream modelling, support beyond a stated period\u003C/td>\n\u003C/tr>\n\u003C/tbody>\n\u003C/table>\n\u003C!--kg-card-end: html-->\n\u003Cp>The exclusions section is what makes the rest credible. A proposal that only lists what is included reads like optimism.\u003C/p>\u003Cp>One more line worth including on any estimate over a certain size: what the client has to provide, and by when. Source access, credentials, a named technical contact, and a decision on ambiguous business rules. Half of all data project overruns are waiting time, and waiting time that was never named in the estimate becomes your problem.\u003C/p>\u003Ch2 id=\"5-the-recalibration-loop\">5. The recalibration loop\u003C/h2>\u003Cp>An estimation grid built once and never revisited drifts within a year, usually toward optimism.\u003C/p>\u003Cp>After each delivery, record three numbers: the tier you assigned, the effort you estimated, the effort it actually took. It takes 10 minutes per project.\u003C/p>\u003Cp>Every quarter, look at the deltas. You are looking for three patterns.\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>Systematic underestimation in one tier:\u003C/strong> Adjust the reference figure. This is the normal outcome and it is why the loop exists.\u003C/li>\u003Cli>\u003Cstrong>A tier that no longer separates cleanly\u003C/strong>: If your standard and complex tiers now overlap, your criteria need work, or the shape of your incoming sources has changed.\u003C/li>\u003Cli>\u003Cstrong>A misclassification pattern:\u003C/strong> If the same characteristic keeps pushing sources over their tier, that characteristic belongs in your criteria for the next tier up. The absence of a reliable change indicator is the most common one.\u003C/li>\u003C/ul>\u003Cp>The reward for maintaining this loop is that your contingency shrinks. A practice with three years of calibrated history can quote with a five percent buffer where it once needed thirty, and that difference is either margin or competitiveness, depending on how you choose to spend it.\u003C/p>\u003Ch2 id=\"6-estimating-when-you-have-no-history\">6. Estimating when you have no history\u003C/h2>\u003Cp>The method above assumes around 10 past deliveries. Plenty of practices do not have them, either because the data practice is new or because nobody recorded the effort. Three substitutes, in descending order of reliability.\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>Borrow from an adjacent capability:\u003C/strong> If your team has built integrations, API work, or file processing, that effort is closer to your ingestion effort than any published benchmark. Adjust for the parts that differ and use it as a starting point.\u003C/li>\u003Cli>\u003Cstrong>Run one deliberately instrumented delivery:\u003C/strong> Take a single source, deliver it to your written standard, and measure carefully. One real data point from your own team beats twenty from someone else's. Sell that first one on time and materials precisely so that the measurement is not distorted by pricing pressure.\u003C/li>\u003Cli>\u003Cstrong>Estimate the qualification only, and stage the commitment:\u003C/strong> Quote the qualification phase firmly, and quote the production work as a range with the commitment deferred until qualification completes. Some clients will refuse. Many will accept, because a firm that says \"here is what I can commit to now and here is what I will commit to in five days\" reads as competent rather than evasive.\u003C/li>\u003C/ul>\u003Cp>What not to do: take a figure from a vendor page or a conference talk and treat it as a reference. Those numbers come from a different team, with a different definition of done, on sources you have not seen. Using them produces an estimate with the appearance of rigour and n
1355one of the substance, which is worse than an honest range.\u003C/p>\u003Ch2 id=\"7-three-estimation-traps-specific-to-ingestion\">7. Three estimation traps specific to ingestion\u003C/h2>\u003Cul>\u003Cli>\u003Cstrong>The clean sample:\u003C/strong> Whatever the client sends you as an example is the cleanest data they have. Always ask for a full period rather than a sample, and if you get a sample, ask specifically for the rows that caused problems downstream.\u003C/li>\u003Cli>\u003Cstrong>The invisible change indicator:\u003C/strong> No reliable way of knowing which records changed since the last run. No updated-at column, no change log, no sequence. That leaves full refresh, or comparison logic. Both are significantly more expensive to build and to run than reading one column. This characteristic alone moves more sources up a tier than any other, and it is the one least likely to come up in a scoping conversation.\u003C/li>\u003Cli>\u003Cstrong>Volume that is not volume yet:\u003C/strong> A source that is small today and growing changes tier at a threshold. Ask about growth, and about any planned acquisition or migration that would multiply it. The cost of finding out later is a rebuild, not an adjustment.\u003C/li>\u003C/ul>\u003Ch2 id=\"what-to-take-away\">What to take away\u003C/h2>\u003Cp>To accurately estimate intake, it's not so much a matter of technique as it is of having your own data.\u003C/p>\u003Cp>Reconstruct the actual effort on your last ten to fifteen deliveries and find the natural tier breaks. Write down the classification criteria and not just the figures. Bill a qualification phase if possible and commit against the qualified source rather than the described one. Record estimate versus actual after every delivery, and revise quarterly.\u003C/p>\u003Cp>None of that is difficult. It is just unglamorous, and it is the difference between an estimate you defend and an estimate you trust.\u003C/p>\u003Cp>What you then do with those numbers commercially, including whether to commit them as a fixed price, is a separate decision.\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Chr>\u003Cp>\u003Cem>PrettyWhale.ai produces the complete ingestion deliverable from a source sample: code, tests, quality checks, schemas, configuration and documentation. Two effects on estimation: the definition of done stops varying by engineer, and effort shifts toward qualification and review, which are the parts you can actually scope in advance. If you are working on how your data practice estimates, we can talk.\u003C/em>\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Cdiv class=\"kg-card kg-cta-card kg-cta-bg-white kg-cta-immersive    kg-cta-centered\" data-layout=\"immersive\">\n            \n            \u003Cdiv class=\"kg-cta-content\">\n                \n                \n                    \u003Cdiv class=\"kg-cta-content-inner\">\n                    \n                        \u003Cdiv class=\"kg-cta-text\">\n                            \u003Cp>\u003Cspan style=\"white-space: pre-wrap;\">You have any question on PrettyWhale.ai ? \u003C/span>\u003C/p>\n                        \u003C/div>\n                    \n                    \n                        \u003Ca href=\"https://prettywhale.ai/contact\" class=\"kg-cta-button \" style=\"background-color: #FF6B1A; color: #FFFFFF;\">\n                            Contact us\n                        \u003C/a>\n                        \n                    \u003C/div>\n                \n            \u003C/div>\n        \u003C/div>","published_at":"2026-08-10T06:31:36.000+00:00","reading_time":7,"feature_image":null,"primary_author":{"id":"69f1af43e6e1d40001b214b8","name":"Kevin BIESSY","slug":"kevin","profile_image":null,"cover_image":null,"bio":null,"website":null,"location":null,"facebook":null,"twitter":null,"threads":null,"bluesky":null,"mastodon":null,"tiktok":null,"youtube":null,"instagram":null,"linkedin":null,"meta_title":null,"meta_description":null,"url":"https://prettywhale.ai/author/kevin/"},"tags":[{"id":"6a4d1ef0399f6a0001a8c991","name":"Data Engineering","slug":"data-engineering","description":null,"feature_image":null,"visibility":"public","og_image":null,"og_title":null,"og_description":null,"twitter_image":null,"twitter_title":null,"twitter_description":null,"meta_title":null,"meta_description":null,"codeinjection_head":null,"codeinjection_foot":null,"canonical_url":null,"accent_color":"#FF6B1A","url":"https://prettywhale.ai/tag/data-engineering/"}]},{"title":"Moove-SI and PrettyWhale.ai Team Up on Data & AI","slug":"moove-si-and-prettywhale-ai-team-up","excerpt":"Moove-SI, an integrator specialising in Microsoft solutions, is adding PrettyWhale.ai to its Data & AI offer to speed up the costliest part of data projects.","html":"\u003Ch2 id=\"what-if-ai-could-speed-up-the-most-technical-costly-time-consuming-and-tedious-part-of-data-projects\">\u003Cstrong>What if AI could speed up the most technical, costly, time-consuming, and tedious part of data projects?\u003C/strong>\u003C/h2>\u003Cp>We picture data projects as analysis and dashboards. In practice, most of the time goes somewhere else: collecting the sources, understanding their structure, transforming, validating, making them usable.\u003C/p>\u003Cp>This work is essential, but it’s often time-consuming, complex, and repetitive.\u003C/p>\u003Cp>To tackle this challenge, \u003Cstrong>Moove-SI is partnering with PrettyWhale.ai 🐋\u003C/strong>\u003C/p>\u003Cp>PrettyWhale.ai develops \u003Cstrong>AI engineering\u003C/strong> \u003Cstrong>technology\u003C/strong> \u003Cstrong>specialized in the automated generation of code for data ingestion and integration\u003C/strong>.\u003C/p>\u003Cp>A general-purpose copilot suggests code to a developer. PrettyWhale.ai automates the entire production chain:\u003C/p>\u003Cul>\u003Cli>✅ source data analysis and profiling\u003C/li>\u003Cli>✅ target structure and transformation rules\u003C/li>\u003Cli>✅ generation of ingestion and integration code\u003C/li>\u003Cli>✅ automatic creation of unit and integration tests\u003C/li>\u003Cli>✅ generation of configurations and technical documentation\u003C/li>\u003Cli>✅ quality and consistency validation of the generated code\u003C/li>\u003C/ul>\u003Cp>&nbsp;\u003C/p>\n\u003Cp>The result is \u003Cstrong>production-grade code that\u003C/strong> is \u003Cstrong>readable, testable, and maintainable\u003C/strong>, remains fully under the control of the technical teams, and integrates with each client’s architectures and development standards.\u003C/p>\u003Cp>By integrating this technology into its Data &amp; AI offerings, Moove-SI strengthens its ability to:\u003C/p>\u003Cul>\u003Cli>✅ accelerate project delivery;\u003C/li>\u003Cli>✅ reduce development costs;\u003C/li>\u003Cli>✅ make production methods more reliable and standardized;\u003C/li>\u003Cli>✅ limit technical debt;\u003C/li>\u003Cli>✅ simplify the maintenance and evolution of data pipelines.\u003C/li>\u003C/ul>\u003Cp>&nbsp;\u003C/p>\n\u003Cp>In practice, our Data Engineers spend less time on plumbing and more on what matters: architecture, business understanding, solution design.\u003C/p>\u003Cp>\u003Cstrong>For our clients, this means pipelines delivered faster, at lower cost, with less debt to carry afterwards.\u003C/strong>\u003C/p>\u003Cp>This marks a new milestone for Moove-SI, one we’re excited to reach alongside PrettyWhale.ai 🚀\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Chr>\u003Ch2 id=\"moove-si\">\u003Cstrong>Moove-SI\u003C/strong>\u003C/h2>\u003Cp>\u003Ca href=\"https://moove-si.com/?ref=prettywhale.ai\" rel=\"noreferrer\">
1355Moove-SI\u003C/a> is an integrator specializing in Microsoft solutions. We support French and international companies in their digital transformation, the optimization of their business processes, and the adoption of new applications related to artificial intelligence.\u003C/p>\u003Cp>Thanks to our technical expertise and our understanding of business challenges, we design and deploy tailored solutions in the areas of sales, marketing, customer service, and business management.\u003C/p>\u003Cp>Our goal: to help our clients increase efficiency, improve performance, and take full advantage of the Microsoft ecosystem.\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Chr>\u003Cdiv class=\"kg-card kg-cta-card kg-cta-bg-white kg-cta-immersive    kg-cta-centered\" data-layout=\"immersive\">\n            \n            \u003Cdiv class=\"kg-cta-content\">\n                \n                \n                    \u003Cdiv class=\"kg-cta-content-inner\">\n                    \n                        \u003Cdiv class=\"kg-cta-text\">\n                            \u003Cp>\u003Cspan style=\"white-space: pre-wrap;\">You have any questions on PrettyWhale.ai ? \u003C/span>\u003C/p>\n                        \u003C/div>\n                    \n                    \n                        \u003Ca href=\"https://prettywhale.ai/contact\" class=\"kg-cta-button \" style=\"background-color: #FF6B1A; color: #FFFFFF;\">\n                            Contact us\n                        \u003C/a>\n                        \n                    \u003C/div>\n                \n            \u003C/div>\n        \u003C/div>","published_at":"2026-08-06T13:05:18.000+00:00","reading_time":2,"feature_image":"https://prettywhale.ai/content/images/2026/08/partnership-lockup-2.png","primary_author":{"id":"69f1af43e6e1d40001b214b8","name":"Kevin BIESSY","slug":"kevin","profile_image":null,"cover_image":null,"bio":null,"website":null,"location":null,"facebook":null,"twitter":null,"threads":null,"bluesky":null,"mastodon":null,"tiktok":null,"youtube":null,"instagram":null,"linkedin":null,"meta_title":null,"meta_description":null,"url":"https://prettywhale.ai/author/kevin/"},"tags":[{"id":"6a6b435a393ec400015228a0","name":"Partners","slug":"partenariat","description":null,"feature_image":null,"visibility":"public","og_image":null,"og_title":null,"og_description":null,"twitter_image":null,"twitter_title":null,"twitter_description":null,"meta_title":null,"meta_description":null,"codeinjection_head":null,"codeinjection_foot":null,"canonical_url":null,"accent_color":"#17285D","url":"https://prettywhale.ai/tag/partenariat/"}]},{"title":"Engineering AI, Copilots and Code Generators: A Taxonomy","slug":"engineering-ai-copilots-and-code-generators-a-taxonomy","excerpt":"\"AI writes code now\" covers four product families with four failure modes. The criterion that separates them, and four questions to pick the right one.","html":"\u003Cblockquote>\"AI writes code now\" describes four different products with four different failure modes.\u003C/blockquote>\u003Cp>A team that buys a copilot expecting a deliverable is disappointed. A team that evaluates a code generator the way it evaluates a copilot measures the wrong thing. And a team that assumes any of them removes the need to understand the code they ship learns otherwise, usually in production.\u003C/p>\u003Cp>This is an attempt at a taxonomy. Not a ranking, because the families answer different questions, and the honest answer to \"which one\" is usually \"which of these problems do you have\".\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Ch2 id=\"1-why-the-vocabulary-is-broken\">1. Why the vocabulary is broken\u003C/h2>\u003Cp>Four words do most of the damage:\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>\"Assistant\"\u003C/strong> and \u003Cstrong>\"copilot\"\u003C/strong> are used both for tools that complete a line as you type and for tools that produce a whole file from a description. Those are different products.\u003C/li>\u003Cli>\u003Cstrong>\"Agent\"\u003C/strong> now means anything from a loop around a chat model to a system that opens a pull request. The word tells you nothing about what you receive.\u003C/li>\u003Cli>\u003Cstrong>\"Generation\"\u003C/strong> covers both a snippet and a runnable project with tests. The gap between those is most of the work.\u003C/li>\u003C/ul>\u003Cp>The result is that vendor comparisons happen at the level of model quality, which is the least discriminating variable available, because everyone has access to similar models.\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Ch2 id=\"2-the-criterion-that-actually-separates-the-families\">2. The criterion that actually separates the families\u003C/h2>\u003Cp>The useful question is not how the tool works. It is what it hands you, and who owns what happens next.\u003C/p>\u003Cp>Two dimensions:\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>Unit of output.\u003C/strong> A completion, a file, a project, or a running service.\u003C/li>\u003Cli>\u003Cstrong>Who carries the integration burden.\u003C/strong> You, or the tool.\u003C/li>\u003C/ul>\u003Cp>That second dimension is the one that gets ignored, and it is where the cost lives. A tool that produces good code you then have to wire in, test, document and make deployable has handed you a starting point. Whether that is a good deal depends entirely on how much of the remaining work you were going to do anyway.\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Ch2 id=\"3-the-four-families\">3. The four families\u003C/h2>\n\u003C!--kg-card-begin: html-->\n\u003Ctable style=\"width:100%;border-collapse:collapse;\">\n  \u003Cthead>\n    \u003Ctr>\n      \u003Cth style=\"border:1px solid #94a3b8;padding:10px 12px;text-align:left;\">
1355Family\u003C/th>\n      \u003Cth style=\"border:1px solid #94a3b8;padding:10px 12px;text-align:left;\">Unit of output\u003C/th>\n      \u003Cth style=\"border:1px solid #94a3b8;padding:10px 12px;text-align:left;\">Integration burden\u003C/th>\n      \u003Cth style=\"border:1px solid #94a3b8;padding:10px 12px;text-align:left;\">Typical examples\u003C/th>\n    \u003C/tr>\n  \u003C/thead>\n  \u003Ctbody>\n    \u003Ctr>\n      \u003Ctd style=\"border:1px solid #94a3b8;padding:10px 12px;\">\u003Cstrong>In-editor completion\u003C/strong>\u003C/td>\n      \u003Ctd style=\"border:1px solid #94a3b8;padding:10px 12px;\">A line, a block, a function\u003C/td>\n      \u003Ctd style=\"border:1px solid #94a3b8;padding:10px 12px;\">Entirely on you\u003C/td>\n      \u003Ctd style=\"border:1px solid #94a3b8;padding:10px 12px;\">GitHub Copilot, Cursor tab completion, JetBrains AI\u003C/td>\n    \u003C/tr>\n    \u003Ctr>\n      \u003Ctd style=\"border:1px solid #94a3b8;padding:10px 12px;\">\u003Cstrong>Conversational generation - Copilots\u003C/strong>\u003C/td>\n      \u003Ctd style=\"border:1px solid #94a3b8;padding:10px 12px;\">A file, a snippet, an explanation\u003C/td>\n      \u003Ctd style=\"border:1px solid #94a3b8;padding:10px 12px;\">Entirely on you\u003C/td>\n      \u003Ctd style=\"border:1px solid #94a3b8;padding:10px 12px;\">Claude Code, Antigravity 2.0, Cline, Cursor\u003C/td>\n    \u003C/tr>\n    \u003Ctr>\n      \u003Ctd style=\"border:1px solid #94a3b8;padding:10px 12px;\">\u003Cstrong>Specialized generation - Engineering AI\u003C/strong>\u003C/td>\n      \u003Ctd style=\"border:1px solid #94a3b8;padding:10px 12px;\">A complete project or component, with its supporting artefacts\u003C/td>\n      \u003Ctd style=\"border:1px solid #94a3b8;padding:10px 12px;\">Mostly on the tool\u003C/td>\n      \u003Ctd style=\"border:1px solid #94a3b8;padding:10px 12px;\">Domain-specific generators, PrettyWhale.AI in the ingestion domain\u003C/td>\n    \u003C/tr>\n    \u003Ctr>\n      \u003Ctd style=\"border:1px solid #94a3b8;padding:10px 12px;\">\u003Cstrong>Managed platform\u003C/strong>\u003C/td>\n      \u003Ctd style=\"border:1px solid #94a3b8;padding:10px 12px;\">A running service, no code exposed\u003C/td>\n      \u003Ctd style=\"border:1px solid #94a3b8;padding:10px 12px;\">On the vendor, and so is the lock-in\u003C/td>\n      \u003Ctd style=\"border:1px solid #94a3b8;padding:10px 12px;\">Fivetran, Airbyte Cloud, hyperscaler managed services\u003C/td>\n    \u003C/tr>\n  \u003C/tbody>\n\u003C/table>\n\u003C!--kg-card-end: html-->\n\u003Cp>The two middle families get conflated most often, and they differ on the dimension that matters. A conversational model produces plausible code fast, and leaves you to determine whether it handles the cases you did not mention. A specialized generator produces a narrower range of things, and is accountable for the artefacts around the code.\u003C/p>\u003Cp>The fourth family is included deliberately, because for a large share of data ingestion work it is the correct answer.\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Ch2 id=\"4-what-each-family-is-genuinely-good-at\">4. What each family is genuinely good at\u003C/h2>\u003Cp>\u003Cstrong>In-editor completion.\u003C/strong> Best-in-class at reducing keystrokes on code you already know how to write. The value is real and it compounds across a working day. What it is not good at is the code you do not know how to write, because you cannot evaluate a suggestion in a domain you are unfamiliar with. That is the failure mode: a plausible completion in unfamiliar territory is more dangerous than no completion.\u003C/p>\u003Cp>\u003Cstrong>Conversational generation.\u003C/strong> Best-in-class at breadth. Any language, any framework, any problem, immediately, plus an explanation. Unbeatable for exploration, for unfamiliar syntax, for a first pass at something you will then rewrite. The failure mode is the one everyone has met: code that runs on the example and quietly omits the handling of everything the prompt did not mention. It optimizes for a working demonstration, because that is what it was rewarded for.\u003C/p>\u003Cp>\u003Cstrong>Specialized generation.\u003C/strong> Best at producing the full artefact set for a bounded problem, because the boundary is what makes the completeness possible. If a system only ever produces one kind of thing, it can be accountable for the tests, the schema and the documentation of that thing. The trade is scope: outside its domain it does nothing. And the honest risk is that a complete-looking deliverable invites less scrutiny than a snippet.\u003C/p>\u003Cp>\u003Cstrong>Managed platform.\u003C/strong> Best when the source is in the catalogue and you would rather not own the code. That combination covers a lot of real situations, and treating it as a compromise is a mistake. The failure modes are known and they are commercial rather than technical: cost at volume, and the sources the catalogue does not cover.\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Ch2 id=\"5-each-family-fails-a-different-evaluation\">5. Each family fails a different evaluation\u003C/h2>\u003Cp>The most common tooling mistake is not choosing the wrong family. It is evaluating one family with the criteria of another, which produces a confident decision based on a measurement that did not apply.\u003C/p>\n\u003C!--kg-card-begin: html-->\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Family\u003C/th>\n\u003Cth>
1355The wrong test\u003C/th>\n\u003Cth>The test that tells you something\u003C/th>\n\u003C/tr>\n\u003C/thead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>In-editor completion\u003C/strong>\u003C/td>\n\u003Ctd>Asking it to produce a whole feature\u003C/td>\n\u003Ctd>Have two engineers work a normal day with and without it, in code they know\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Conversational generation - Copilots\u003C/strong>\u003C/td>\n\u003Ctd>Judging the first answer to a well-written prompt\u003C/td>\n\u003Ctd>Take the output and count what you had to add before it could ship\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Specialized generation - Engineering AI\u003C/strong>\u003C/td>\n\u003Ctd>Comparing its snippet quality against a general model\u003C/td>\n\u003Ctd>Run it on a source you have already delivered by hand, and compare the full artefact set against what you shipped\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Cstrong>Managed platform\u003C/strong>\u003C/td>\n\u003Ctd>Testing it on a source in the catalogue\u003C/td>\n\u003Ctd>Testing it on your three most awkward sources, and pricing it at next year's volume\u003C/td>\n\u003C/tr>\n\u003C/tbody>\n\u003C/table>\n\u003C!--kg-card-end: html-->\n\u003Cp>&nbsp;\u003C/p>\n\u003Ch2 id=\"6-choosing-four-questions\">6. Choosing: four questions\u003C/h2>\u003Cp>Not \"which tool is best\", but \"which of these is true for you\".\u003C/p>\u003Cp>\u003Cstrong>Is the code you need in a domain your team knows well?\u003C/strong> If yes, completion tools give you the most value per euro, and generation adds process overhead for little gain.\u003C/p>\u003Cp>\u003Cstrong>Are your sources in a managed platform's catalogue?\u003C/strong> If yes, and you have no strong portability requirement, buy the platform. Building what you can buy is a common and expensive error.\u003C/p>\u003Cp>\u003Cstrong>Do you need to own and deploy the code?\u003C/strong> Compliance, air-gapped environments, exit-risk policies, or wanting to modify behaviour without waiting for a vendor. If yes, the managed family is out regardless of its other merits.\u003C/p>\u003Cp>\u003Cstrong>Is the work repetitive across many similar instances?\u003C/strong> Fifty sources of the same shape is where specialized generation earns its keep. Three sources is probably not worth changing your process for.\u003C/p>\u003Cblockquote>\u003Cstrong>A note on how each family is priced\u003C/strong>\u003Cbr>Pricing model shapes the decision more than most evaluations account for, because it determines who absorbs the cost of growth. Completion tools are priced per seat, which makes the cost predictable and independent of how much you use them. \u003Cbr>Conversational tools are priced per seat or per token, and the token variant means your bill moves with your usage in ways that are hard to forecast. Specialized generators tend to be priced per unit of output or per seat, which ties cost to work delivered. Some managed platforms are priced on data volume, the model that surprises teams most often, because volume grows on its own and the bill grows with it.\u003Cbr>The practical consequence: a comparison run at today's volume can invert entirely at next year's. Volume pricing is defensible for a managed service, but it's a reason to run the arithmetic at the scale you expect rather than the scale you have.\u003C/blockquote>\u003Cp>&nbsp;\u003C/p>\n\u003Ch2 id=\"7-where-the-categories-are-converging\">7. Where the categories are converging\u003C/h2>\u003Cp>Two movements worth watching, because they will make this taxonomy less useful over time.\u003C/p>\u003Cp>\u003Cstrong>Completion tools are moving up.\u003C/strong> Editor-integrated agents now open branches and produce multi-file changes. The unit of output is growing, which pushes them toward the generation families and raises the same question about accountability for tests and documentation.\u003C/p>\u003Cp>\u003Cstrong>Managed platforms are moving toward code.\u003C/strong> Several now offer connector development frameworks, which is an admission that the catalogue will never be complete. That narrows the gap from the other direction.\u003C/p>\u003Cp>What will not converge is the criterion in section 2. Whatever the tools are called in two years, the question of what you receive and who owns the remaining work will still separate them, and it will still be the question vendor comparisons avoid.\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Ch2 id=\"what-this-taxonomy-leaves-out\">What this taxonomy leaves out\u003C/h2>\u003Cp>Two things, named because a classification that pretends to be complete is less useful than one that states its edges.\u003C/p>\u003Cp>\u003Cstrong>It says nothing about model quality.\u003C/strong> Deliberately. Model capability moves every few months and it is broadly shared across vendors, so a taxonomy built on it would be obsolete before it was published. What persists is the shape of what you receive.\u003C/p>\u003Cp>\u003Cstrong>It does not cover tools that generate things other than code.\u003C/strong> Test generation, documentation generation, schema inference, and query assistants all exist and none of them fit cleanly here. A team assembling a toolchain will end up combining families and categories, which is normal and not a flaw in the choice.\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Ch2 id=\"what-to-take-away\">What to take away\u003C/h2>\u003Cp>Four families, separated by unit of output and by who carries the integration burden. Completion for code you already know. Conversation for breadth and exploration. Specialized generation for repetitive, bounded, artefact-heavy work. Managed platforms when the catalogue covers you and ownership does not matter.\u003C/p>\u003Cp>Most teams need more than one. The mistake is not picking wrong, it is picking a family whose failure mode you have not thought about.\u003C/p>\u003Cp>And the question that outranks all of it: whoever reviews the output, do they know what a production-grade version of this looks like. No family removes that requirement, and the ones that produce the most complete-looking output are the ones where forgetting it costs the most.\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Chr>\u003Cp>\u003Cem>PrettyWhale.ai sits in the third family, for one domain: ingestion and integration code. Given a source sample it produces the code, unit tests, data quality checks, schemas, configuration, orchestration and documentation, as portable code you own.\u003C/em>\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Cdiv class=\"kg-card kg-cta-card kg-cta-bg-white kg-cta-immersive    kg-cta-centered\" data-layout=\"immersive\">\n            \n            \u003Cdiv class=\"kg-cta-content\">\n                \n                \n                    \u003Cdiv class=\"kg-cta-content-inner\">\n                    \n                        \u003Cdiv class=\"kg-cta-text\">\n                            \u003Cp>\u003Cspan style=\"white-space: pre-wrap;\">You have any question on PrettyWhale.ai ? \u003C/span>\u003C/p>\n                        \u003C/div>\n                    \n                    \n                        \u003Ca href=\"https://prettywhale.ai/contact\" class=\"kg-cta-button \" style=\"background-color: #FF6B1A;
1355 color: #FFFFFF;\">\n                            Contact us\n                        \u003C/a>\n                        \n                    \u003C/div>\n                \n            \u003C/div>\n        \u003C/div>","published_at":"2026-08-05T14:20:22.000+00:00","reading_time":6,"feature_image":null,"primary_author":{"id":"69f1af43e6e1d40001b214b8","name":"Kevin BIESSY","slug":"kevin","profile_image":null,"cover_image":null,"bio":null,"website":null,"location":null,"facebook":null,"twitter":null,"threads":null,"bluesky":null,"mastodon":null,"tiktok":null,"youtube":null,"instagram":null,"linkedin":null,"meta_title":null,"meta_description":null,"url":"https://prettywhale.ai/author/kevin/"},"tags":[{"id":"6a4d1f89399f6a0001a8c99b","name":"Artificial Intelligence","slug":"artificial-intelligence","description":null,"feature_image":null,"visibility":"public","og_image":null,"og_title":null,"og_description":null,"twitter_image":null,"twitter_title":null,"twitter_description":null,"meta_title":null,"meta_description":null,"codeinjection_head":null,"codeinjection_foot":null,"canonical_url":null,"accent_color":"#FFC13C","url":"https://prettywhale.ai/tag/artificial-intelligence/"}]},{"title":"The End of the Daily-Rate Model for IT Services","slug":"the-end-of-the-it-services-company-model-based-solely-on-daily-rates","excerpt":"For thirty years IT services firms sold human time by the day. Clients now ask what you save them, not how many consultants you can put on the project.","html":"\u003Cp>For more than thirty years, the economic model of IT services companies (ESNs in France) has relied on a simple equation: selling human time. The arrival of AI profoundly changes this equation. When an engineer can produce in a few hours what previously required several days, a question becomes inevitable: Should the client continue to buy time or start buying results?\u003C/p>\u003Cp>The paradox is striking: The more productive an IT services company becomes thanks to AI, the fewer days it theoretically needs to sell. For the first time in the history of digital services, the production of part of the technical work is no longer exclusively linked to the time spent by an engineer.\u003C/p>\u003Cp>AI is capable of producing code, generating tests, writing documentation, creating configurations, performing data analysis, or even automating tasks previously performed manually. In this new context, the end client is no longer interested in the number of days consumed. They are interested in the result obtained.\u003C/p>\u003Cp>The question is no longer:\u003C/p>\u003Cp>➡️&nbsp;\"How many consultants are you going to mobilize?\"\u003C/p>\u003Cp>but:\u003C/p>\u003Cp>➡️&nbsp;\"How much time are you going to save me?\"\u003C/p>\u003Cp>and above all:\u003C/p>\u003Cp>➡️&nbsp;\"How much savings are you going to allow me to achieve?\"\u003C/p>\u003Cp>This evolution directly challenges the historic Daily Rate (TJM) model. Indeed, when a task previously performed in twenty days can be executed in two days thanks to AI, the client no longer understands why they should continue to pay for twenty days of service. The billing logic based exclusively on time then progressively becomes incompatible with the productivity gains brought by artificial intelligence.\u003C/p>\u003Cp>IT services companies that continue to sell only time risk finding themselves in a paradoxical situation: the more productive their teams become thanks to AI, the more they mechanically reduce their volume of billable days. Conversely, companies that know how to sell value rather than time will have a considerable advantage.\u003C/p>\u003Cp>Tomorrow, the most successful economic models will probably be hybrid, with a portion linked to:\u003C/p>\u003Cul>\u003Cli>&nbsp;Human expertise;\u003C/li>\u003Cli>The use of AI platforms;\u003C/li>\u003Cli>&nbsp;The results obtained;\u003C/li>\u003Cli>&nbsp;The actual uses of the technology.\u003C/li>\u003C/ul>\u003Cp>The IT services company will no longer be just a supplier of consultants. It will become a productivity operator. Its mission will no longer consist of selling man-days, but of delivering a business result faster, at a lower cost, and with a higher level of quality.\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Cdiv class=\"kg-card kg-cta-card kg-cta-bg-white kg-cta-immersive    kg-cta-centered\" data-layout=\"immersive\">\n            \n            \u003Cdiv class=\"kg-cta-content\">\n                \n                \n                    \u003Cdiv class=\"kg-cta-content-inner\">\n                    \n                        \u003Cdiv class=\"kg-cta-text\">\n                            \u003Cp>\u003Cspan style=\"white-space: pre-wrap;\">You have any questions on PrettyWhale.ai ? \u003C/span>\u003C/p>\n                        \u003C/div>\n                    \n                    \n                        \u003Ca href=\"https://prettywhale.ai/contact\" class=\"kg-cta-button \" style=\"background-color: #FF6B1A;
1355 color: #FFFFFF;\">\n                            Contact us\n                        \u003C/a>\n                        \n                    \u003C/div>\n                \n            \u003C/div>\n        \u003C/div>","published_at":"2026-06-17T11:05:14.000+00:00","reading_time":2,"feature_image":"https://prettywhale.ai/content/images/2026/06/image--3--1.png","primary_author":{"id":"69f1af43e6e1d40001b214b8","name":"Kevin BIESSY","slug":"kevin","profile_image":null,"cover_image":null,"bio":null,"website":null,"location":null,"facebook":null,"twitter":null,"threads":null,"bluesky":null,"mastodon":null,"tiktok":null,"youtube":null,"instagram":null,"linkedin":null,"meta_title":null,"meta_description":null,"url":"https://prettywhale.ai/author/kevin/"},"tags":[{"id":"6a4d1ef0399f6a0001a8c991","name":"Data Engineering","slug":"data-engineering","description":null,"feature_image":null,"visibility":"public","og_image":null,"og_title":null,"og_description":null,"twitter_image":null,"twitter_title":null,"twitter_description":null,"meta_title":null,"meta_description":null,"codeinjection_head":null,"codeinjection_foot":null,"canonical_url":null,"accent_color":"#FF6B1A","url":"https://prettywhale.ai/tag/data-engineering/"}]},{"title":"Ingestion vs Integration: A Confusion That Costs Millions","slug":"ingestion-vs-integration-code","excerpt":"Ingestion and integration code get used as synonyms. They solve different problems, and the teams that optimise the wrong one pay for it in production.","html":"\u003Cp>In many Data projects, the terms ingestion and integration are used as synonyms. \u003C/p>\u003Cp>Yet, they designate two radically different realities. \u003C/p>\u003Cp>And this confusion is at the root of a large part of the cost overruns, delays, and quality problems that companies face today.\u003C/p>\u003Cp>\u003C/p>\u003Ch2 id=\"ingestion-code-the-foundation\">Ingestion code: the foundation\u003C/h2>\u003Cp>The ingestion code is responsible for collecting, controlling, standardizing, and preparing data as soon as it enters the information system. It is what:\u003C/p>\u003Cul>\u003Cli>reads files, APIs, databases, or external streams;\u003C/li>\u003Cli>detects anomalies;\u003C/li>\u003Cli>applies quality rules;\u003C/li>\u003Cli>converts formats;\u003C/li>\u003Cli>manages errors;\u003C/li>\u003Cli>guarantees that the data entering is consistent and usable.\u003C/li>\u003C/ul>\u003Cp>In other words, ingestion determines the quality of the raw material.\u003C/p>\u003Cp>\u003C/p>\u003Ch2 id=\"integration-code-the-assembly\">Integration code: the assembly\u003C/h2>\u003Cp>The integration code comes into play next. Its role is to combine, enrich, reconcile, and distribute data from different sources. For example, it allows to:\u003C/p>\u003Cul>\u003Cli>cross-reference CRM and ERP data;\u003C/li>\u003Cli>feed a Data Warehouse;\u003C/li>\u003Cli>produce business views;\u003C/li>\u003Cli>feed applications or AI models.\u003C/li>\u003C/ul>\u003Cp>Integration does not create quality. It simply leverages what it is provided with.\u003C/p>\u003Cp>\u003C/p>\u003Ch2 id=\"the-problem-many-companies-optimize-the-wrong-subject\">The problem: many companies optimize the wrong subject\u003C/h2>\u003Cp>When erroneous, incomplete, or inconsistent data enters the system, no integration can magically transform it into reliable data. Poor ingestion produces:\u003C/p>\u003Cul>\u003Cli>inconsistent repositories;\u003C/li>\u003Cli>incorrect calculations;\u003C/li>\u003Cli>misleading dashboards;\u003C/li>\u003Cli>biased AI models;\u003C/li>\u003Cli>skyrocketing maintenance costs.\u003C/li>\u003C/ul>\u003Cp>Integration then only accelerates the propagation of errors.\u003C/p>\u003Cp>\u003C/p>\u003Ch2 id=\"garbage-in-garbage-out\">\"Garbage In, Garbage Out\"\u003C/h2>\u003Cp>This decades-old computer science rule still holds true. If the ingestion is bad:\u003C/p>\u003Cul>\u003Cli>the Data Warehouse will be bad;\u003C/li>\u003Cli>the indicators will be bad;\u003C/li>\u003Cli>the analyses will be bad;\u003C/li>\u003Cli>the AI will be bad.\u003C/li>\u003C/ul>\u003Cp>Regardless of the sophistication of the tools used afterward. No technological layer can sustainably compensate for data that is degraded upon arrival.\u003C/p>\u003Cp>\u003C/p>\u003Ch2 id=\"why-ingestion-is-becoming-a-strategic-stake\">Why ingestion is becoming a strategic stake\u003C/h2>\u003Cp>For years, the Data industry has invested heavily in:\u003C/p>\u003Cul>\u003Cli>orchestration;\u003C/li>\u003Cli>governance;\u003C/li>\u003Cli>cataloging;\u003C/li>\u003Cli>observability;\u003C/li>\u003Cli>artificial intelligence.\u003C/li>\u003C/ul>\u003Cp>But the real starting point remains the same: the quality of the ingestion code. Because it is what determines whether the data entering the company is reliable or not. An organization that masters its ingestion reduces its costs, accelerates its Data projects, and mechanically increases trust in its analyses and AI systems.\u003C/p>\u003Cp>\u003C/p>\u003Ch2 id=\"conclusion\">Conclusion\u003C/h2>\u003Cp>We can build the best integrations in the world. We can deploy the most modern platforms. We can connect hun
1355dreds of systems. But one truth remains: an excellent integration applied to a poor ingestion only produces bad data faster.\u003C/p>\u003Cp>The quality of a Data system does not begin with integration. It begins with ingestion. Integration transports data. Ingestion creates its quality. If the second fails, the first only accelerates the disaster.\u003C/p>\u003Cp>&nbsp;\u003C/p>\n\u003Cdiv class=\"kg-card kg-cta-card kg-cta-bg-white kg-cta-immersive    kg-cta-centered\" data-layout=\"immersive\">\n            \n            \u003Cdiv class=\"kg-cta-content\">\n                \n                \n                    \u003Cdiv class=\"kg-cta-content-inner\">\n                    \n                        \u003Cdiv class=\"kg-cta-text\">\n                            \u003Cp>\u003Cspan style=\"white-space: pre-wrap;\">You have any questions on PrettyWhale.ai ? \u003C/span>\u003C/p>\n                        \u003C/div>\n                    \n                    \n                        \u003Ca href=\"https://prettywhale.ai/contact\" class=\"kg-cta-button \" style=\"background-color: #FF6B1A; color: #FFFFFF;\">\n                            Contact us\n                        \u003C/a>\n                        \n                    \u003C/div>\n                \n            \u003C/div>\n        \u003C/div>","published_at":"2026-06-12T10:00:00.000+00:00","reading_time":2,"feature_image":"https://prettywhale.ai/content/images/2026/06/Capture-d-----cran-2026-06-16-175101.png","primary_author":{"id":"69f1af43e6e1d40001b214b8","name":"Kevin BIESSY","slug":"kevin","profile_image":null,"cover_image":null,"bio":null,"website":null,"location":null,"facebook":null,"twitter":null,"threads":null,"bluesky":null,"mastodon":null,"tiktok":null,"youtube":null,"instagram":null,"linkedin":null,"meta_title":null,"meta_description":null,"url":"https://prettywhale.ai/author/kevin/"},"tags":[{"id":"6a4d1ef0399f6a0001a8c991","name":"Data Engineering","slug":"data-engineering","description":null,"feature_image":null,"visibility":"public","og_image":null,"og_title":null,"og_description":null,"twitter_image":null,"twitter_title":null,"twitter_description":null,"meta_title":null,"meta_description":null,"codeinjection_head":null,"codeinjection_foot":null,"canonical_url":null,"accent_color":"#FF6B1A","url":"https://prettywhale.ai/tag/data-engineering/"}]}],"headers":{"headers":{},"normalizedNames":{},"lazyUpdate":null},"status":200,"statusText":"OK","url":"/api/posts"},"__nghData__":[{"t":{"21":"t0"},"c":{"21":[{"i":"t0","r":1,"t":{"1":"t1","2":"t2"},"c":{"1":[],"2":[{"i":"t2","r":1}]},"x":4},{"i":"t0","r":1,"t":{"1":"t1","2":"t2"},"c":{"1":[{"i":"t1","r":1}],"2":[]}}]}},{},{"t":{"16":"t3"},"c":{"16":[{"i":"t3","r":1,"x":5}]}},{"t":{"7":"t4"},"c":{"7":[{"i":"t4","r":1,"x":4}]}},{"t":{"2":"t5","3":"t8"},"c":{"2":[{"i":"t5","r":3,"t":{"0":"t6","1":"t7"},"c":{"0":[],"1":[{"i":"t7","r":1}]},"x":5}],"3":[]}},{"t":{"1":"t10","5":"t11","8":"t13"},"c":{"1":[{"i":"t10","r":1}],"5":[{"i":"t11","r":1,"t":{"1":"t12"},"c":{"1":[{"i":"t12","r":2}]}}],"8":[]}},{"t":{"10":"t9"},"c":{"10":[{"i":"t9","r":1,"x":12}]}},{"t":{"14":"t14"},"c":{"14":[{"i":"t14","r":3,"t":{"0":"t15","1":"t16"},"c":{"0":[],"1":[{"i":"t16","r":1}]},"x":4},{"i":"t14","r":3,"t":{"0":"t15","1":"t16"},"c":{"0":[{"i":"t15","r":1}],"1":[]}}]}},{"c":{"0":[{"i":"c411397059","r":1}]}}]}</script>
1355</body></html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.