@ petetasker @ptasker Refactoring the Disaster No One Is Talking About deliciousbrains.com/loopconf
How We're Handling JavaScript Tech Debt in WP Migrate DB Pro
@ petetasker @ptasker ABOUT ME • WP Migrate DB Pro developer Canadian, Eh! 🍂 • First conference talk 😲 •
WP Migrate DB Pro
Outline: • What is technical debt? • Symptoms • Understand the code • Get your test on! • Refactor
What disaster 🔦 ?
What is tech debt? Easy > Better™
$('.migrate-db-button').click(function (event) { doing_ajax = true; $.ajax({ success: function (data) { doing_ajax = false; wpmdb.functions.long_func = function () { doing_ajax = true; $.ajax({ // ... }); }; } }); // end ajax });
$('.migrate-db-button').click(function (event) { doing_ajax = true; $.ajax({ success: function (data) { doing_ajax = false; wpmdb.functions.long_func = function () { doing_ajax = true; $.ajax({ // ... }); }; } }); // end ajax });
$('.migrate-db-button').click(function (event) { doing_ajax = true; $.ajax({ success: function (data) { doing_ajax = false; wpmdb.functions.long_func = function () { doing_ajax = true; $.ajax({ // ... }); }; } }); // end ajax });
How does this happen?
Symptoms • Long functions, deeply nested code • The “the only one who can ever change this code is Pete 😭 ” e fg ect • Global variables and functions
💩
Understanding the code 🤕
Get your test on 💼
To get this…
Requires this… You need this…
const findText = await page.evaluate( ( sel ) => { return document.querySelector( sel ).value; }, '#old-url' ); expect( findText ).to.be.equal( `//${process.env.REMOTE_SITE}` ); await page.waitFor( '[value=Pull]' ); await page.click( '[value=Pull]' ); const successText = await page.evaluate( ( sel ) => { return document.querySelector( sel ).innerHTML; }, '.progress-title' ); //Check header for string expect( successText ).to.include( process.env.REMOTE_SITE );
const findText = await page.evaluate( ( sel ) => { return document.querySelector( sel ).value; }, '#old-url' ); expect( findText ).to.be.equal( `//${process.env.REMOTE_SITE}` ); await page.waitFor( '[value=Pull]' ); await page.click( '[value=Pull]' ); const successText = await page.evaluate( ( sel ) => { return document.querySelector( sel ).innerHTML; }, '.progress-title' ); //Check header for string expect( successText ).to.include( process.env.REMOTE_SITE );
const findText = await page.evaluate( ( sel ) => { return document.querySelector( sel ).value; }, '#old-url' ); expect( findText ).to.be.equal( `//${process.env.REMOTE_SITE}` ); await page.waitFor( '[value=Pull]' ); await page.click( '[value=Pull]' ); const successText = await page.evaluate( ( sel ) => { return document.querySelector( sel ).innerHTML; }, '.progress-title' ); //Check header for string expect( successText ).to.include( process.env.REMOTE_SITE );
Refactor • Globals and ‘this’ • Code splitting • ‘New JS™’
dev.to
function wpmdb_call_next_hook( ) { const wpmdb = window.wpmdb; if ( !wpmdb.common.call_stack.length ) { wpmdb.common.call_stack = wpmdb.common.hooks; } var func = wpmdb.common.call_stack[ 0 ]; wpmdb.common.call_stack.shift(); func.call( this ); } Window {}
// ‘use strict’ function wpmdb_call_next_hook( ) { const wpmdb = window.wpmdb; if ( !wpmdb.common.call_stack.length ) { wpmdb.common.call_stack = wpmdb.common.hooks; } var func = wpmdb.common.call_stack[ 0 ]; wpmdb.common.call_stack.shift(); func.call( this ); } undefined
// ‘use strict’ function wpmdb_call_next_hook( ) { const wpmdb = window.wpmdb; if ( !wpmdb.common.call_stack.length ) { wpmdb.common.call_stack = wpmdb.common.hooks; } var func = wpmdb.common.call_stack[ 0 ]; wpmdb.common.call_stack.shift(); func.call( this ); } window.wpmdb_call_next_hook(); Window {}
Code Splitting (or why I ❤ Webpack)
/** * Wrapper for wpmdb.functions, assigned in Webpack entry file */ export default class WPMDBCommon { constructor() {} // Previously wpmdb.functions.wpmdb_migration_type() wpmdb_toggle_migration_action_text() { // ... } // ... }
import WPMDBCommon from './src/js/helpers/wpmdbCommon'; // window.wpmdb.functions = {…} wpmdb.functions = new WPMDBCommon(); function importAll( r ) { r.keys().forEach( function( file ) { if ( !file.includes( 'wpmdbCommon.js' ) ) { r( file ); } } ); } importAll( require.context( 'imports-loader?$=>window.jQuery!./src', true, /^(.*\.(js|sass|scss$))/im ) );
import WPMDBCommon from './src/js/helpers/wpmdbCommon'; // window.wpmdb.functions = {…} wpmdb.functions = new WPMDBCommon(); function importAll( r ) { r.keys().forEach( function( file ) { if ( !file.includes( 'wpmdbCommon.js' ) ) { r( file ); } } ); } importAll( require.context( 'imports-loader?$=>window.jQuery!./src', true, /^(.*\.(js|sass|scss$))/im ) );
import WPMDBCommon from './src/js/helpers/wpmdbCommon'; // window.wpmdb.functions = {…} wpmdb.functions = new WPMDBCommon(); function importAll( r ) { r.keys().forEach( function( file ) { if ( !file.includes( 'wpmdbCommon.js' ) ) { r( file ); } } ); } importAll( require.context( 'imports-loader?$=>window.jQuery!./src', true, /^(.*\.(js|sass|scss$))/im ) );
import WPMDBCommon from './src/js/helpers/wpmdbCommon'; // window.wpmdb.functions = {…} wpmdb.functions = new WPMDBCommon(); function importAll( r ) { r.keys().forEach( function( file ) { if ( !file.includes( 'wpmdbCommon.js' ) ) { r( file ); } } ); } importAll( require.context( 'imports-loader?$=>window.jQuery!./src', true, /^(.*\.(js|sass|scss$))/im ) );
‘New JS™’ 😄
// Before $( $table_select ).append( '<option' + selected + 'value="' + table + '">' + table + size + '</option>' ); // After $( $table_select ).append( `<option ${selected} value="${table}"> ${table + size} </option>` );
async recursiveTransferFiles( stage, status = null, batch_count = 10 ) { let response; try { response = await $.ajax( { // ... } ); const parsed_response = JSON.parse( response ); // Do stuff with response } catch ( error ) { // … } }
async recursiveTransferFiles( stage, status = null, batch_count = 10 ) { let response; try { response = await $.ajax( { // ... } ); const parsed_response = JSON.parse( response ); // Do stuff with response } catch ( error ) { // … } }
async recursiveTransferFiles( stage, status = null, batch_count = 10 ) { let response; try { response = await $.ajax( { // ... } ); const parsed_response = JSON.parse( response ); // Do stuff with response } catch ( error ) { // … } }
async recursiveTransferFiles( stage, status = null, batch_count = 10 ) { let response; try { response = await $.ajax( { // ... } ); const parsed_response = JSON.parse( response ); // Do stuff with response } catch ( error ) { // … } }
async recursiveTransferFiles( stage, status = null, batch_count = 10 ) { let response; try { response = await $.ajax( { // ... } ); const parsed_response = JSON.parse( response ); // Do stuff with response } catch ( error ) { // … } }
Remember: • Understand the code • Get your test on! • Refactor Globals • Code splitting • ‘New JS™’ •
@ petetasker @ptasker That’s all folks deliciousbrains.com/loopconf
Recommend
More recommend