Creating and populating the textures is largely the same.
It’s when you go to use the textures things differ, if you are using shaders. You still bind textures to texture units as before, but instead of telling the hardware how to generate the shader (e.g.:
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
this being one small piece of the “shader” logic), you write the shader yourself, give it to the driver, and then bind it to make it active. Your shader is coded with the logic to sample specific texture units and combine them in some way you find useful.